VirtualBox

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

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

Main/Machine: fix default VM frontend handling, the empty string was misinterpreted when the VM was terminated as "we didn't start this process", suppressing the process reaping

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 488.5 KB
Line 
1/* $Id: MachineImpl.cpp 46387 2013-06-04 14:54:10Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#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#include <iprt/base64.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#include "VBox/com/MultiResult.h"
92
93#include <algorithm>
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureFile = "Test.webm";
171 mVideoCaptureWidth = 1024;
172 mVideoCaptureHeight = 768;
173 mVideoCaptureRate = 512;
174 mVideoCaptureFps = 25;
175 mVideoCaptureEnabled = false;
176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
177 maVideoCaptureScreens[i] = true;
178
179 mHWVirtExEnabled = true;
180 mHWVirtExNestedPagingEnabled = true;
181#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
182 mHWVirtExLargePagesEnabled = true;
183#else
184 /* Not supported on 32 bits hosts. */
185 mHWVirtExLargePagesEnabled = false;
186#endif
187 mHWVirtExVPIDEnabled = true;
188 mHWVirtExUXEnabled = true;
189 mHWVirtExForceEnabled = false;
190#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
191 mHWVirtExExclusive = false;
192#else
193 mHWVirtExExclusive = true;
194#endif
195#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
196 mPAEEnabled = true;
197#else
198 mPAEEnabled = false;
199#endif
200 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
201 mSyntheticCpu = false;
202 mHPETEnabled = false;
203
204 /* default boot order: floppy - DVD - HDD */
205 mBootOrder[0] = DeviceType_Floppy;
206 mBootOrder[1] = DeviceType_DVD;
207 mBootOrder[2] = DeviceType_HardDisk;
208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
209 mBootOrder[i] = DeviceType_Null;
210
211 mClipboardMode = ClipboardMode_Disabled;
212 mDragAndDropMode = DragAndDropMode_Disabled;
213 mGuestPropertyNotificationPatterns = "";
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mEmulatedUSBWebcamEnabled = FALSE;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227
228 /* Maximum CPU execution cap by default. */
229 mCpuExecutionCap = 100;
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine::HDData structure
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::MediaData::MediaData()
241{
242}
243
244Machine::MediaData::~MediaData()
245{
246}
247
248/////////////////////////////////////////////////////////////////////////////
249// Machine class
250/////////////////////////////////////////////////////////////////////////////
251
252// constructor / destructor
253/////////////////////////////////////////////////////////////////////////////
254
255Machine::Machine()
256 : mCollectorGuest(NULL),
257 mPeer(NULL),
258 mParent(NULL),
259 mSerialPorts(),
260 mParallelPorts(),
261 uRegistryNeedsSaving(0)
262{}
263
264Machine::~Machine()
265{}
266
267HRESULT Machine::FinalConstruct()
268{
269 LogFlowThisFunc(("\n"));
270 return BaseFinalConstruct();
271}
272
273void Machine::FinalRelease()
274{
275 LogFlowThisFunc(("\n"));
276 uninit();
277 BaseFinalRelease();
278}
279
280/**
281 * Initializes a new machine instance; this init() variant creates a new, empty machine.
282 * This gets called from VirtualBox::CreateMachine().
283 *
284 * @param aParent Associated parent object
285 * @param strConfigFile Local file system path to the VM settings file (can
286 * be relative to the VirtualBox config directory).
287 * @param strName name for the machine
288 * @param llGroups list of groups for the machine
289 * @param aOsType OS Type of this machine or NULL.
290 * @param aId UUID for the new machine.
291 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
292 *
293 * @return Success indicator. if not S_OK, the machine object is invalid
294 */
295HRESULT Machine::init(VirtualBox *aParent,
296 const Utf8Str &strConfigFile,
297 const Utf8Str &strName,
298 const StringsList &llGroups,
299 GuestOSType *aOsType,
300 const Guid &aId,
301 bool fForceOverwrite,
302 bool fDirectoryIncludesUUID)
303{
304 LogFlowThisFuncEnter();
305 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
306
307 /* Enclose the state transition NotReady->InInit->Ready */
308 AutoInitSpan autoInitSpan(this);
309 AssertReturn(autoInitSpan.isOk(), E_FAIL);
310
311 HRESULT rc = initImpl(aParent, strConfigFile);
312 if (FAILED(rc)) return rc;
313
314 rc = tryCreateMachineConfigFile(fForceOverwrite);
315 if (FAILED(rc)) return rc;
316
317 if (SUCCEEDED(rc))
318 {
319 // create an empty machine config
320 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
321
322 rc = initDataAndChildObjects();
323 }
324
325 if (SUCCEEDED(rc))
326 {
327 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
328 mData->mAccessible = TRUE;
329
330 unconst(mData->mUuid) = aId;
331
332 mUserData->s.strName = strName;
333
334 mUserData->s.llGroups = llGroups;
335
336 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
337 // the "name sync" flag determines whether the machine directory gets renamed along
338 // with the machine file; say so if the settings file name is the same as the
339 // settings file parent directory (machine directory)
340 mUserData->s.fNameSync = isInOwnDir();
341
342 // initialize the default snapshots folder
343 rc = COMSETTER(SnapshotFolder)(NULL);
344 AssertComRC(rc);
345
346 if (aOsType)
347 {
348 /* Store OS type */
349 mUserData->s.strOsType = aOsType->id();
350
351 /* Apply BIOS defaults */
352 mBIOSSettings->applyDefaults(aOsType);
353
354 /* Apply network adapters defaults */
355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
356 mNetworkAdapters[slot]->applyDefaults(aOsType);
357
358 /* Apply serial port defaults */
359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
360 mSerialPorts[slot]->applyDefaults(aOsType);
361
362 /* Let the OS type select 64-bit ness. */
363 mHWData->mLongMode = aOsType->is64Bit()
364 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 }
366
367 /* At this point the changing of the current state modification
368 * flag is allowed. */
369 allowStateModification();
370
371 /* commit all changes made during the initialization */
372 commit();
373 }
374
375 /* Confirm a successful initialization when it's the case */
376 if (SUCCEEDED(rc))
377 {
378 if (mData->mAccessible)
379 autoInitSpan.setSucceeded();
380 else
381 autoInitSpan.setLimited();
382 }
383
384 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
385 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
386 mData->mRegistered,
387 mData->mAccessible,
388 rc));
389
390 LogFlowThisFuncLeave();
391
392 return rc;
393}
394
395/**
396 * Initializes a new instance with data from machine XML (formerly Init_Registered).
397 * Gets called in two modes:
398 *
399 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
400 * UUID is specified and we mark the machine as "registered";
401 *
402 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
403 * and the machine remains unregistered until RegisterMachine() is called.
404 *
405 * @param aParent Associated parent object
406 * @param aConfigFile Local file system path to the VM settings file (can
407 * be relative to the VirtualBox config directory).
408 * @param aId UUID of the machine or NULL (see above).
409 *
410 * @return Success indicator. if not S_OK, the machine object is invalid
411 */
412HRESULT Machine::initFromSettings(VirtualBox *aParent,
413 const Utf8Str &strConfigFile,
414 const Guid *aId)
415{
416 LogFlowThisFuncEnter();
417 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
418
419 /* Enclose the state transition NotReady->InInit->Ready */
420 AutoInitSpan autoInitSpan(this);
421 AssertReturn(autoInitSpan.isOk(), E_FAIL);
422
423 HRESULT rc = initImpl(aParent, strConfigFile);
424 if (FAILED(rc)) return rc;
425
426 if (aId)
427 {
428 // loading a registered VM:
429 unconst(mData->mUuid) = *aId;
430 mData->mRegistered = TRUE;
431 // now load the settings from XML:
432 rc = registeredInit();
433 // this calls initDataAndChildObjects() and loadSettings()
434 }
435 else
436 {
437 // opening an unregistered VM (VirtualBox::OpenMachine()):
438 rc = initDataAndChildObjects();
439
440 if (SUCCEEDED(rc))
441 {
442 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
443 mData->mAccessible = TRUE;
444
445 try
446 {
447 // load and parse machine XML; this will throw on XML or logic errors
448 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
449
450 // reject VM UUID duplicates, they can happen if someone
451 // tries to register an already known VM config again
452 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
453 true /* fPermitInaccessible */,
454 false /* aDoSetError */,
455 NULL) != VBOX_E_OBJECT_NOT_FOUND)
456 {
457 throw setError(E_FAIL,
458 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
459 mData->m_strConfigFile.c_str());
460 }
461
462 // use UUID from machine config
463 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
464
465 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
466 NULL /* puuidRegistry */);
467 if (FAILED(rc)) throw rc;
468
469 /* At this point the changing of the current state modification
470 * flag is allowed. */
471 allowStateModification();
472
473 commit();
474 }
475 catch (HRESULT err)
476 {
477 /* we assume that error info is set by the thrower */
478 rc = err;
479 }
480 catch (...)
481 {
482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
483 }
484 }
485 }
486
487 /* Confirm a successful initialization when it's the case */
488 if (SUCCEEDED(rc))
489 {
490 if (mData->mAccessible)
491 autoInitSpan.setSucceeded();
492 else
493 {
494 autoInitSpan.setLimited();
495
496 // uninit media from this machine's media registry, or else
497 // reloading the settings will fail
498 mParent->unregisterMachineMedia(getId());
499 }
500 }
501
502 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
503 "rc=%08X\n",
504 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
505 mData->mRegistered, mData->mAccessible, rc));
506
507 LogFlowThisFuncLeave();
508
509 return rc;
510}
511
512/**
513 * Initializes a new instance from a machine config that is already in memory
514 * (import OVF case). Since we are importing, the UUID in the machine
515 * config is ignored and we always generate a fresh one.
516 *
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 allowStateModification();
571
572 /* commit all changes made during the initialization */
573 commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 autoInitSpan.setLimited();
585
586 // uninit media from this machine's media registry, or else
587 // reloading the settings will fail
588 mParent->unregisterMachineMedia(getId());
589 }
590 }
591
592 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
593 "rc=%08X\n",
594 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
595 mData->mRegistered, mData->mAccessible, rc));
596
597 LogFlowThisFuncLeave();
598
599 return rc;
600}
601
602/**
603 * Shared code between the various init() implementations.
604 * @param aParent
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::registeredInit()
697{
698 AssertReturn(!isSessionMachine(), E_FAIL);
699 AssertReturn(!isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->settingsFilePath().c_str());
725
726 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
764 mData->mUuid.raw(),
765 mData->mAccessError.getText().raw()));
766
767 /* rollback all changes */
768 rollback(false /* aNotify */);
769
770 // uninit media from this machine's media registry, or else
771 // reloading the settings will fail
772 mParent->unregisterMachineMedia(getId());
773
774 /* uninitialize the common part to make sure all data is reset to
775 * default (null) values */
776 uninitDataAndChildObjects();
777
778 rc = S_OK;
779 }
780
781 return rc;
782}
783
784/**
785 * Uninitializes the instance.
786 * Called either from FinalRelease() or by the parent when it gets destroyed.
787 *
788 * @note The caller of this method must make sure that this object
789 * a) doesn't have active callers on the current thread and b) is not locked
790 * by the current thread; otherwise uninit() will hang either a) due to
791 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
792 * a dead-lock caused by this thread waiting for all callers on the other
793 * threads are done but preventing them from doing so by holding a lock.
794 */
795void Machine::uninit()
796{
797 LogFlowThisFuncEnter();
798
799 Assert(!isWriteLockOnCurrentThread());
800
801 Assert(!uRegistryNeedsSaving);
802 if (uRegistryNeedsSaving)
803 {
804 AutoCaller autoCaller(this);
805 if (SUCCEEDED(autoCaller.rc()))
806 {
807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
808 saveSettings(NULL, Machine::SaveS_Force);
809 }
810 }
811
812 /* Enclose the state transition Ready->InUninit->NotReady */
813 AutoUninitSpan autoUninitSpan(this);
814 if (autoUninitSpan.uninitDone())
815 return;
816
817 Assert(!isSnapshotMachine());
818 Assert(!isSessionMachine());
819 Assert(!!mData);
820
821 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
822 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
823
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825
826 if (!mData->mSession.mMachine.isNull())
827 {
828 /* Theoretically, this can only happen if the VirtualBox server has been
829 * terminated while there were clients running that owned open direct
830 * sessions. Since in this case we are definitely called by
831 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
832 * won't happen on the client watcher thread (because it does
833 * VirtualBox::addCaller() for the duration of the
834 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
835 * cannot happen until the VirtualBox caller is released). This is
836 * important, because SessionMachine::uninit() cannot correctly operate
837 * after we return from this method (it expects the Machine instance is
838 * still valid). We'll call it ourselves below.
839 */
840 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
841 (SessionMachine*)mData->mSession.mMachine));
842
843 if (Global::IsOnlineOrTransient(mData->mMachineState))
844 {
845 LogWarningThisFunc(("Setting state to Aborted!\n"));
846 /* set machine state using SessionMachine reimplementation */
847 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
848 }
849
850 /*
851 * Uninitialize SessionMachine using public uninit() to indicate
852 * an unexpected uninitialization.
853 */
854 mData->mSession.mMachine->uninit();
855 /* SessionMachine::uninit() must set mSession.mMachine to null */
856 Assert(mData->mSession.mMachine.isNull());
857 }
858
859 // uninit media from this machine's media registry, if they're still there
860 Guid uuidMachine(getId());
861
862 /* the lock is no more necessary (SessionMachine is uninitialized) */
863 alock.release();
864
865 /* XXX This will fail with
866 * "cannot be closed because it is still attached to 1 virtual machines"
867 * because at this point we did not call uninitDataAndChildObjects() yet
868 * and therefore also removeBackReference() for all these mediums was not called! */
869
870 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
871 mParent->unregisterMachineMedia(uuidMachine);
872
873 // has machine been modified?
874 if (mData->flModifications)
875 {
876 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
877 rollback(false /* aNotify */);
878 }
879
880 if (mData->mAccessible)
881 uninitDataAndChildObjects();
882
883 /* free the essential data structure last */
884 mData.free();
885
886 LogFlowThisFuncLeave();
887}
888
889// IMachine properties
890/////////////////////////////////////////////////////////////////////////////
891
892STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
893{
894 CheckComArgOutPointerValid(aParent);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 /* mParent is constant during life time, no need to lock */
900 ComObjPtr<VirtualBox> pVirtualBox(mParent);
901 pVirtualBox.queryInterfaceTo(aParent);
902
903 return S_OK;
904}
905
906STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
907{
908 CheckComArgOutPointerValid(aAccessible);
909
910 AutoLimitedCaller autoCaller(this);
911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
912
913 LogFlowThisFunc(("ENTER\n"));
914
915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
916
917 HRESULT rc = S_OK;
918
919 if (!mData->mAccessible)
920 {
921 /* try to initialize the VM once more if not accessible */
922
923 AutoReinitSpan autoReinitSpan(this);
924 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
925
926#ifdef DEBUG
927 LogFlowThisFunc(("Dumping media backreferences\n"));
928 mParent->dumpAllBackRefs();
929#endif
930
931 if (mData->pMachineConfigFile)
932 {
933 // reset the XML file to force loadSettings() (called from registeredInit())
934 // to parse it again; the file might have changed
935 delete mData->pMachineConfigFile;
936 mData->pMachineConfigFile = NULL;
937 }
938
939 rc = registeredInit();
940
941 if (SUCCEEDED(rc) && mData->mAccessible)
942 {
943 autoReinitSpan.setSucceeded();
944
945 /* make sure interesting parties will notice the accessibility
946 * state change */
947 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
948 mParent->onMachineDataChange(mData->mUuid);
949 }
950 }
951
952 if (SUCCEEDED(rc))
953 *aAccessible = mData->mAccessible;
954
955 LogFlowThisFuncLeave();
956
957 return rc;
958}
959
960STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
961{
962 CheckComArgOutPointerValid(aAccessError);
963
964 AutoLimitedCaller autoCaller(this);
965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
966
967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
968
969 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
970 {
971 /* return shortly */
972 aAccessError = NULL;
973 return S_OK;
974 }
975
976 HRESULT rc = S_OK;
977
978 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
979 rc = errorInfo.createObject();
980 if (SUCCEEDED(rc))
981 {
982 errorInfo->init(mData->mAccessError.getResultCode(),
983 mData->mAccessError.getInterfaceID().ref(),
984 Utf8Str(mData->mAccessError.getComponent()).c_str(),
985 Utf8Str(mData->mAccessError.getText()));
986 rc = errorInfo.queryInterfaceTo(aAccessError);
987 }
988
989 return rc;
990}
991
992STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
993{
994 CheckComArgOutPointerValid(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1000
1001 mUserData->s.strName.cloneTo(aName);
1002
1003 return S_OK;
1004}
1005
1006STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1007{
1008 CheckComArgStrNotEmptyOrNull(aName);
1009
1010 AutoCaller autoCaller(this);
1011 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1012
1013 // prohibit setting a UUID only as the machine name, or else it can
1014 // never be found by findMachine()
1015 Guid test(aName);
1016
1017 if (test.isValid())
1018 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1019
1020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1021
1022 HRESULT rc = checkStateDependency(MutableStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strName = aName;
1028
1029 return S_OK;
1030}
1031
1032STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1033{
1034 CheckComArgOutPointerValid(aDescription);
1035
1036 AutoCaller autoCaller(this);
1037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1038
1039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1040
1041 mUserData->s.strDescription.cloneTo(aDescription);
1042
1043 return S_OK;
1044}
1045
1046STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1047{
1048 AutoCaller autoCaller(this);
1049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1050
1051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 // this can be done in principle in any state as it doesn't affect the VM
1054 // significantly, but play safe by not messing around while complex
1055 // activities are going on
1056 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1057 if (FAILED(rc)) return rc;
1058
1059 setModified(IsModified_MachineData);
1060 mUserData.backup();
1061 mUserData->s.strDescription = aDescription;
1062
1063 return S_OK;
1064}
1065
1066STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1067{
1068 CheckComArgOutPointerValid(aId);
1069
1070 AutoLimitedCaller autoCaller(this);
1071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1072
1073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1074
1075 mData->mUuid.toUtf16().cloneTo(aId);
1076
1077 return S_OK;
1078}
1079
1080STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1081{
1082 CheckComArgOutSafeArrayPointerValid(aGroups);
1083
1084 AutoCaller autoCaller(this);
1085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1086
1087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1088 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1089 size_t i = 0;
1090 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1091 it != mUserData->s.llGroups.end();
1092 ++it, i++)
1093 {
1094 Bstr tmp = *it;
1095 tmp.cloneTo(&groups[i]);
1096 }
1097 groups.detachTo(ComSafeArrayOutArg(aGroups));
1098
1099 return S_OK;
1100}
1101
1102STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1103{
1104 AutoCaller autoCaller(this);
1105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1106
1107 StringsList llGroups;
1108 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1109 if (FAILED(rc))
1110 return rc;
1111
1112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 // changing machine groups is possible while the VM is offline
1115 rc = checkStateDependency(OfflineStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.llGroups = llGroups;
1121
1122 return S_OK;
1123}
1124
1125STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1126{
1127 CheckComArgOutPointerValid(aOSTypeId);
1128
1129 AutoCaller autoCaller(this);
1130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1131
1132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1133
1134 mUserData->s.strOsType.cloneTo(aOSTypeId);
1135
1136 return S_OK;
1137}
1138
1139STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1140{
1141 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1142
1143 AutoCaller autoCaller(this);
1144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1145
1146 /* look up the object by Id to check it is valid */
1147 ComPtr<IGuestOSType> guestOSType;
1148 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 /* when setting, always use the "etalon" value for consistency -- lookup
1152 * by ID is case-insensitive and the input value may have different case */
1153 Bstr osTypeId;
1154 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1155 if (FAILED(rc)) return rc;
1156
1157 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1158
1159 rc = checkStateDependency(MutableStateDep);
1160 if (FAILED(rc)) return rc;
1161
1162 setModified(IsModified_MachineData);
1163 mUserData.backup();
1164 mUserData->s.strOsType = osTypeId;
1165
1166 return S_OK;
1167}
1168
1169
1170STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1171{
1172 CheckComArgOutPointerValid(aFirmwareType);
1173
1174 AutoCaller autoCaller(this);
1175 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1176
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aFirmwareType = mHWData->mFirmwareType;
1180
1181 return S_OK;
1182}
1183
1184STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1185{
1186 AutoCaller autoCaller(this);
1187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mFirmwareType = aFirmwareType;
1196
1197 return S_OK;
1198}
1199
1200STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1201{
1202 CheckComArgOutPointerValid(aKeyboardHIDType);
1203
1204 AutoCaller autoCaller(this);
1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1206
1207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1210
1211 return S_OK;
1212}
1213
1214STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1215{
1216 AutoCaller autoCaller(this);
1217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 HRESULT rc = checkStateDependency(MutableStateDep);
1221 if (FAILED(rc)) return rc;
1222
1223 setModified(IsModified_MachineData);
1224 mHWData.backup();
1225 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1226
1227 return S_OK;
1228}
1229
1230STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1231{
1232 CheckComArgOutPointerValid(aPointingHIDType);
1233
1234 AutoCaller autoCaller(this);
1235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1236
1237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1238
1239 *aPointingHIDType = mHWData->mPointingHIDType;
1240
1241 return S_OK;
1242}
1243
1244STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1245{
1246 AutoCaller autoCaller(this);
1247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1248 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 HRESULT rc = checkStateDependency(MutableStateDep);
1251 if (FAILED(rc)) return rc;
1252
1253 setModified(IsModified_MachineData);
1254 mHWData.backup();
1255 mHWData->mPointingHIDType = aPointingHIDType;
1256
1257 return S_OK;
1258}
1259
1260STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1261{
1262 CheckComArgOutPointerValid(aChipsetType);
1263
1264 AutoCaller autoCaller(this);
1265 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1266
1267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 *aChipsetType = mHWData->mChipsetType;
1270
1271 return S_OK;
1272}
1273
1274STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1275{
1276 AutoCaller autoCaller(this);
1277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 HRESULT rc = checkStateDependency(MutableStateDep);
1281 if (FAILED(rc)) return rc;
1282
1283 if (aChipsetType != mHWData->mChipsetType)
1284 {
1285 setModified(IsModified_MachineData);
1286 mHWData.backup();
1287 mHWData->mChipsetType = aChipsetType;
1288
1289 // Resize network adapter array, to be finalized on commit/rollback.
1290 // We must not throw away entries yet, otherwise settings are lost
1291 // without a way to roll back.
1292 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1293 size_t oldCount = mNetworkAdapters.size();
1294 if (newCount > oldCount)
1295 {
1296 mNetworkAdapters.resize(newCount);
1297 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1298 {
1299 unconst(mNetworkAdapters[slot]).createObject();
1300 mNetworkAdapters[slot]->init(this, slot);
1301 }
1302 }
1303 }
1304
1305 return S_OK;
1306}
1307
1308STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1309{
1310 CheckComArgOutPointerValid(aHWVersion);
1311
1312 AutoCaller autoCaller(this);
1313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1314
1315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1316
1317 mHWData->mHWVersion.cloneTo(aHWVersion);
1318
1319 return S_OK;
1320}
1321
1322STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1323{
1324 /* check known version */
1325 Utf8Str hwVersion = aHWVersion;
1326 if ( hwVersion.compare("1") != 0
1327 && hwVersion.compare("2") != 0)
1328 return setError(E_INVALIDARG,
1329 tr("Invalid hardware version: %ls\n"), aHWVersion);
1330
1331 AutoCaller autoCaller(this);
1332 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1333
1334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1335
1336 HRESULT rc = checkStateDependency(MutableStateDep);
1337 if (FAILED(rc)) return rc;
1338
1339 setModified(IsModified_MachineData);
1340 mHWData.backup();
1341 mHWData->mHWVersion = hwVersion;
1342
1343 return S_OK;
1344}
1345
1346STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1347{
1348 CheckComArgOutPointerValid(aUUID);
1349
1350 AutoCaller autoCaller(this);
1351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1352
1353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1354
1355 if (mHWData->mHardwareUUID.isValid())
1356 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1357 else
1358 mData->mUuid.toUtf16().cloneTo(aUUID);
1359
1360 return S_OK;
1361}
1362
1363STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1364{
1365 Guid hardwareUUID(aUUID);
1366 if (!hardwareUUID.isValid())
1367 return E_INVALIDARG;
1368
1369 AutoCaller autoCaller(this);
1370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1371
1372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1373
1374 HRESULT rc = checkStateDependency(MutableStateDep);
1375 if (FAILED(rc)) return rc;
1376
1377 setModified(IsModified_MachineData);
1378 mHWData.backup();
1379 if (hardwareUUID == mData->mUuid)
1380 mHWData->mHardwareUUID.clear();
1381 else
1382 mHWData->mHardwareUUID = hardwareUUID;
1383
1384 return S_OK;
1385}
1386
1387STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1388{
1389 CheckComArgOutPointerValid(memorySize);
1390
1391 AutoCaller autoCaller(this);
1392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1393
1394 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 *memorySize = mHWData->mMemorySize;
1397
1398 return S_OK;
1399}
1400
1401STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1402{
1403 /* check RAM limits */
1404 if ( memorySize < MM_RAM_MIN_IN_MB
1405 || memorySize > MM_RAM_MAX_IN_MB
1406 )
1407 return setError(E_INVALIDARG,
1408 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1409 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1410
1411 AutoCaller autoCaller(this);
1412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mMemorySize = memorySize;
1422
1423 return S_OK;
1424}
1425
1426STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1427{
1428 CheckComArgOutPointerValid(CPUCount);
1429
1430 AutoCaller autoCaller(this);
1431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1432
1433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1434
1435 *CPUCount = mHWData->mCPUCount;
1436
1437 return S_OK;
1438}
1439
1440STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1441{
1442 /* check CPU limits */
1443 if ( CPUCount < SchemaDefs::MinCPUCount
1444 || CPUCount > SchemaDefs::MaxCPUCount
1445 )
1446 return setError(E_INVALIDARG,
1447 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1448 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1449
1450 AutoCaller autoCaller(this);
1451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1452
1453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1454
1455 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1456 if (mHWData->mCPUHotPlugEnabled)
1457 {
1458 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1459 {
1460 if (mHWData->mCPUAttached[idx])
1461 return setError(E_INVALIDARG,
1462 tr("There is still a CPU attached to socket %lu."
1463 "Detach the CPU before removing the socket"),
1464 CPUCount, idx+1);
1465 }
1466 }
1467
1468 HRESULT rc = checkStateDependency(MutableStateDep);
1469 if (FAILED(rc)) return rc;
1470
1471 setModified(IsModified_MachineData);
1472 mHWData.backup();
1473 mHWData->mCPUCount = CPUCount;
1474
1475 return S_OK;
1476}
1477
1478STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1479{
1480 CheckComArgOutPointerValid(aExecutionCap);
1481
1482 AutoCaller autoCaller(this);
1483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1484
1485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 *aExecutionCap = mHWData->mCpuExecutionCap;
1488
1489 return S_OK;
1490}
1491
1492STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1493{
1494 HRESULT rc = S_OK;
1495
1496 /* check throttle limits */
1497 if ( aExecutionCap < 1
1498 || aExecutionCap > 100
1499 )
1500 return setError(E_INVALIDARG,
1501 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1502 aExecutionCap, 1, 100);
1503
1504 AutoCaller autoCaller(this);
1505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1506
1507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1508
1509 alock.release();
1510 rc = onCPUExecutionCapChange(aExecutionCap);
1511 alock.acquire();
1512 if (FAILED(rc)) return rc;
1513
1514 setModified(IsModified_MachineData);
1515 mHWData.backup();
1516 mHWData->mCpuExecutionCap = aExecutionCap;
1517
1518 /* Save settings if online - todo why is this required?? */
1519 if (Global::IsOnline(mData->mMachineState))
1520 saveSettings(NULL);
1521
1522 return S_OK;
1523}
1524
1525
1526STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1527{
1528 CheckComArgOutPointerValid(aEnabled);
1529
1530 AutoCaller autoCaller(this);
1531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1532
1533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
1535 *aEnabled = mHWData->mCPUHotPlugEnabled;
1536
1537 return S_OK;
1538}
1539
1540STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1541{
1542 HRESULT rc = S_OK;
1543
1544 AutoCaller autoCaller(this);
1545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1546
1547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1548
1549 rc = checkStateDependency(MutableStateDep);
1550 if (FAILED(rc)) return rc;
1551
1552 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1553 {
1554 if (aEnabled)
1555 {
1556 setModified(IsModified_MachineData);
1557 mHWData.backup();
1558
1559 /* Add the amount of CPUs currently attached */
1560 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1561 {
1562 mHWData->mCPUAttached[i] = true;
1563 }
1564 }
1565 else
1566 {
1567 /*
1568 * We can disable hotplug only if the amount of maximum CPUs is equal
1569 * to the amount of attached CPUs
1570 */
1571 unsigned cCpusAttached = 0;
1572 unsigned iHighestId = 0;
1573
1574 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1575 {
1576 if (mHWData->mCPUAttached[i])
1577 {
1578 cCpusAttached++;
1579 iHighestId = i;
1580 }
1581 }
1582
1583 if ( (cCpusAttached != mHWData->mCPUCount)
1584 || (iHighestId >= mHWData->mCPUCount))
1585 return setError(E_INVALIDARG,
1586 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1587
1588 setModified(IsModified_MachineData);
1589 mHWData.backup();
1590 }
1591 }
1592
1593 mHWData->mCPUHotPlugEnabled = aEnabled;
1594
1595 return rc;
1596}
1597
1598STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1599{
1600#ifdef VBOX_WITH_USB_CARDREADER
1601 CheckComArgOutPointerValid(aEnabled);
1602
1603 AutoCaller autoCaller(this);
1604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1605
1606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1607
1608 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1609
1610 return S_OK;
1611#else
1612 NOREF(aEnabled);
1613 return E_NOTIMPL;
1614#endif
1615}
1616
1617STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1618{
1619#ifdef VBOX_WITH_USB_CARDREADER
1620 AutoCaller autoCaller(this);
1621 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT rc = checkStateDependency(MutableStateDep);
1625 if (FAILED(rc)) return rc;
1626
1627 setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1630
1631 return S_OK;
1632#else
1633 NOREF(aEnabled);
1634 return E_NOTIMPL;
1635#endif
1636}
1637
1638STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1639{
1640#ifdef VBOX_WITH_USB_VIDEO
1641 CheckComArgOutPointerValid(aEnabled);
1642
1643 AutoCaller autoCaller(this);
1644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1645
1646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1647
1648 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1649
1650 return S_OK;
1651#else
1652 NOREF(aEnabled);
1653 return E_NOTIMPL;
1654#endif
1655}
1656
1657STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1658{
1659#ifdef VBOX_WITH_USB_VIDEO
1660 AutoCaller autoCaller(this);
1661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1663
1664 HRESULT rc = checkStateDependency(MutableStateDep);
1665 if (FAILED(rc)) return rc;
1666
1667 setModified(IsModified_MachineData);
1668 mHWData.backup();
1669 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1670
1671 return S_OK;
1672#else
1673 NOREF(aEnabled);
1674 return E_NOTIMPL;
1675#endif
1676}
1677
1678STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1679{
1680 CheckComArgOutPointerValid(aEnabled);
1681
1682 AutoCaller autoCaller(this);
1683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 *aEnabled = mHWData->mHPETEnabled;
1687
1688 return S_OK;
1689}
1690
1691STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1692{
1693 HRESULT rc = S_OK;
1694
1695 AutoCaller autoCaller(this);
1696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1698
1699 rc = checkStateDependency(MutableStateDep);
1700 if (FAILED(rc)) return rc;
1701
1702 setModified(IsModified_MachineData);
1703 mHWData.backup();
1704
1705 mHWData->mHPETEnabled = aEnabled;
1706
1707 return rc;
1708}
1709
1710STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1711{
1712 AutoCaller autoCaller(this);
1713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1714
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716
1717 *fEnabled = mHWData->mVideoCaptureEnabled;
1718 return S_OK;
1719}
1720
1721STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1722{
1723 AutoCaller autoCaller(this);
1724 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1725
1726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1727 mHWData->mVideoCaptureEnabled = fEnabled;
1728 return S_OK;
1729}
1730
1731STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1732{
1733 CheckComArgOutSafeArrayPointerValid(aScreens);
1734
1735 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1736 for (unsigned i = 0; i < screens.size(); i++)
1737 screens[i] = mHWData->maVideoCaptureScreens[i];
1738 screens.detachTo(ComSafeArrayOutArg(aScreens));
1739 return S_OK;
1740}
1741
1742STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1743{
1744 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1745 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1746 for (unsigned i = 0; i < screens.size(); i++)
1747 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1748 return S_OK;
1749}
1750
1751STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1752{
1753 AutoCaller autoCaller(this);
1754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1755
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757 mHWData->mVideoCaptureFile.cloneTo(apFile);
1758 return S_OK;
1759}
1760
1761STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1762{
1763 Utf8Str strFile(aFile);
1764 AutoCaller autoCaller(this);
1765 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1766
1767 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1768 if (strFile.isEmpty())
1769 strFile = "VideoCap.webm";
1770 mHWData->mVideoCaptureFile = strFile;
1771 return S_OK;
1772}
1773
1774STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1775{
1776 AutoCaller autoCaller(this);
1777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1778
1779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1780 *aHorzRes = mHWData->mVideoCaptureWidth;
1781 return S_OK;
1782}
1783
1784STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1785{
1786 AutoCaller autoCaller(this);
1787 if (FAILED(autoCaller.rc()))
1788 {
1789 LogFlow(("Autolocked failed\n"));
1790 return autoCaller.rc();
1791 }
1792
1793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1794 mHWData->mVideoCaptureWidth = aHorzRes;
1795 return S_OK;
1796}
1797
1798STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1799{
1800 AutoCaller autoCaller(this);
1801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1802
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804 *aVertRes = mHWData->mVideoCaptureHeight;
1805 return S_OK;
1806}
1807
1808STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1809{
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814 mHWData->mVideoCaptureHeight = aVertRes;
1815 return S_OK;
1816}
1817
1818STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1819{
1820 AutoCaller autoCaller(this);
1821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1822
1823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1824 *aRate = mHWData->mVideoCaptureRate;
1825 return S_OK;
1826}
1827
1828STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1829{
1830 AutoCaller autoCaller(this);
1831 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1832
1833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1834 mHWData->mVideoCaptureRate = aRate;
1835 return S_OK;
1836}
1837
1838STDMETHODIMP Machine::COMGETTER(VideoCaptureFps)(ULONG *aFps)
1839{
1840 AutoCaller autoCaller(this);
1841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1842
1843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1844 *aFps = mHWData->mVideoCaptureFps;
1845 return S_OK;
1846}
1847
1848STDMETHODIMP Machine::COMSETTER(VideoCaptureFps)(ULONG aFps)
1849{
1850 AutoCaller autoCaller(this);
1851 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1852
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 mHWData->mVideoCaptureFps = aFps;
1855 return S_OK;
1856}
1857
1858STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1859{
1860 CheckComArgOutPointerValid(aGraphicsControllerType);
1861
1862 AutoCaller autoCaller(this);
1863 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1864
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1868
1869 return S_OK;
1870}
1871
1872STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1873{
1874 switch (aGraphicsControllerType)
1875 {
1876 case GraphicsControllerType_Null:
1877 case GraphicsControllerType_VBoxVGA:
1878 break;
1879 default:
1880 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1881 }
1882
1883 AutoCaller autoCaller(this);
1884 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1885
1886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1887
1888 HRESULT rc = checkStateDependency(MutableStateDep);
1889 if (FAILED(rc)) return rc;
1890
1891 setModified(IsModified_MachineData);
1892 mHWData.backup();
1893 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1894
1895 return S_OK;
1896}
1897
1898STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1899{
1900 CheckComArgOutPointerValid(memorySize);
1901
1902 AutoCaller autoCaller(this);
1903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1904
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 *memorySize = mHWData->mVRAMSize;
1908
1909 return S_OK;
1910}
1911
1912STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1913{
1914 /* check VRAM limits */
1915 if (memorySize < SchemaDefs::MinGuestVRAM ||
1916 memorySize > SchemaDefs::MaxGuestVRAM)
1917 return setError(E_INVALIDARG,
1918 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1919 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1920
1921 AutoCaller autoCaller(this);
1922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1923
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 HRESULT rc = checkStateDependency(MutableStateDep);
1927 if (FAILED(rc)) return rc;
1928
1929 setModified(IsModified_MachineData);
1930 mHWData.backup();
1931 mHWData->mVRAMSize = memorySize;
1932
1933 return S_OK;
1934}
1935
1936/** @todo this method should not be public */
1937STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1938{
1939 CheckComArgOutPointerValid(memoryBalloonSize);
1940
1941 AutoCaller autoCaller(this);
1942 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1943
1944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1945
1946 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1947
1948 return S_OK;
1949}
1950
1951/**
1952 * Set the memory balloon size.
1953 *
1954 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1955 * we have to make sure that we never call IGuest from here.
1956 */
1957STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1958{
1959 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1960#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1961 /* check limits */
1962 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1963 return setError(E_INVALIDARG,
1964 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1965 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1966
1967 AutoCaller autoCaller(this);
1968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1969
1970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1971
1972 setModified(IsModified_MachineData);
1973 mHWData.backup();
1974 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1975
1976 return S_OK;
1977#else
1978 NOREF(memoryBalloonSize);
1979 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1980#endif
1981}
1982
1983STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
1984{
1985 CheckComArgOutPointerValid(aEnabled);
1986
1987 AutoCaller autoCaller(this);
1988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1989
1990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1991
1992 *aEnabled = mHWData->mPageFusionEnabled;
1993 return S_OK;
1994}
1995
1996STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
1997{
1998#ifdef VBOX_WITH_PAGE_SHARING
1999 AutoCaller autoCaller(this);
2000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2001
2002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2003
2004 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2005 setModified(IsModified_MachineData);
2006 mHWData.backup();
2007 mHWData->mPageFusionEnabled = aEnabled;
2008 return S_OK;
2009#else
2010 NOREF(aEnabled);
2011 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2012#endif
2013}
2014
2015STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2016{
2017 CheckComArgOutPointerValid(aEnabled);
2018
2019 AutoCaller autoCaller(this);
2020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2021
2022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2023
2024 *aEnabled = mHWData->mAccelerate3DEnabled;
2025
2026 return S_OK;
2027}
2028
2029STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2030{
2031 AutoCaller autoCaller(this);
2032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2033
2034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2035
2036 HRESULT rc = checkStateDependency(MutableStateDep);
2037 if (FAILED(rc)) return rc;
2038
2039 /** @todo check validity! */
2040
2041 setModified(IsModified_MachineData);
2042 mHWData.backup();
2043 mHWData->mAccelerate3DEnabled = enable;
2044
2045 return S_OK;
2046}
2047
2048
2049STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2050{
2051 CheckComArgOutPointerValid(aEnabled);
2052
2053 AutoCaller autoCaller(this);
2054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2055
2056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2059
2060 return S_OK;
2061}
2062
2063STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2064{
2065 AutoCaller autoCaller(this);
2066 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2067
2068 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2069
2070 HRESULT rc = checkStateDependency(MutableStateDep);
2071 if (FAILED(rc)) return rc;
2072
2073 /** @todo check validity! */
2074
2075 setModified(IsModified_MachineData);
2076 mHWData.backup();
2077 mHWData->mAccelerate2DVideoEnabled = enable;
2078
2079 return S_OK;
2080}
2081
2082STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2083{
2084 CheckComArgOutPointerValid(monitorCount);
2085
2086 AutoCaller autoCaller(this);
2087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2088
2089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2090
2091 *monitorCount = mHWData->mMonitorCount;
2092
2093 return S_OK;
2094}
2095
2096STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2097{
2098 /* make sure monitor count is a sensible number */
2099 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2100 return setError(E_INVALIDARG,
2101 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2102 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2103
2104 AutoCaller autoCaller(this);
2105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2106
2107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2108
2109 HRESULT rc = checkStateDependency(MutableStateDep);
2110 if (FAILED(rc)) return rc;
2111
2112 setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mMonitorCount = monitorCount;
2115
2116 return S_OK;
2117}
2118
2119STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2120{
2121 CheckComArgOutPointerValid(biosSettings);
2122
2123 AutoCaller autoCaller(this);
2124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2125
2126 /* mBIOSSettings is constant during life time, no need to lock */
2127 mBIOSSettings.queryInterfaceTo(biosSettings);
2128
2129 return S_OK;
2130}
2131
2132STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2133{
2134 CheckComArgOutPointerValid(aVal);
2135
2136 AutoCaller autoCaller(this);
2137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2138
2139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 switch (property)
2142 {
2143 case CPUPropertyType_PAE:
2144 *aVal = mHWData->mPAEEnabled;
2145 break;
2146
2147 case CPUPropertyType_Synthetic:
2148 *aVal = mHWData->mSyntheticCpu;
2149 break;
2150
2151 case CPUPropertyType_LongMode:
2152 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2153 *aVal = TRUE;
2154 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2155 *aVal = FALSE;
2156#if HC_ARCH_BITS == 64
2157 else
2158 *aVal = TRUE;
2159#else
2160 else
2161 {
2162 *aVal = FALSE;
2163
2164 ComPtr<IGuestOSType> ptrGuestOSType;
2165 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2166 if (SUCCEEDED(hrc2))
2167 {
2168 BOOL fIs64Bit = FALSE;
2169 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2170 if (SUCCEEDED(hrc2) && fIs64Bit)
2171 {
2172 ComObjPtr<Host> ptrHost = mParent->host();
2173 alock.release();
2174
2175 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2176 if (FAILED(hrc2))
2177 *aVal = FALSE;
2178 }
2179 }
2180 }
2181#endif
2182 break;
2183
2184 default:
2185 return E_INVALIDARG;
2186 }
2187 return S_OK;
2188}
2189
2190STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2191{
2192 AutoCaller autoCaller(this);
2193 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2194
2195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2196
2197 HRESULT rc = checkStateDependency(MutableStateDep);
2198 if (FAILED(rc)) return rc;
2199
2200 switch (property)
2201 {
2202 case CPUPropertyType_PAE:
2203 setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mPAEEnabled = !!aVal;
2206 break;
2207
2208 case CPUPropertyType_Synthetic:
2209 setModified(IsModified_MachineData);
2210 mHWData.backup();
2211 mHWData->mSyntheticCpu = !!aVal;
2212 break;
2213
2214 case CPUPropertyType_LongMode:
2215 setModified(IsModified_MachineData);
2216 mHWData.backup();
2217 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2218 break;
2219
2220 default:
2221 return E_INVALIDARG;
2222 }
2223 return S_OK;
2224}
2225
2226STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2227{
2228 CheckComArgOutPointerValid(aValEax);
2229 CheckComArgOutPointerValid(aValEbx);
2230 CheckComArgOutPointerValid(aValEcx);
2231 CheckComArgOutPointerValid(aValEdx);
2232
2233 AutoCaller autoCaller(this);
2234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2235
2236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2237
2238 switch(aId)
2239 {
2240 case 0x0:
2241 case 0x1:
2242 case 0x2:
2243 case 0x3:
2244 case 0x4:
2245 case 0x5:
2246 case 0x6:
2247 case 0x7:
2248 case 0x8:
2249 case 0x9:
2250 case 0xA:
2251 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2252 return E_INVALIDARG;
2253
2254 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2255 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2256 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2257 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2258 break;
2259
2260 case 0x80000000:
2261 case 0x80000001:
2262 case 0x80000002:
2263 case 0x80000003:
2264 case 0x80000004:
2265 case 0x80000005:
2266 case 0x80000006:
2267 case 0x80000007:
2268 case 0x80000008:
2269 case 0x80000009:
2270 case 0x8000000A:
2271 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2272 return E_INVALIDARG;
2273
2274 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2275 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2276 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2277 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2278 break;
2279
2280 default:
2281 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2282 }
2283 return S_OK;
2284}
2285
2286STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2287{
2288 AutoCaller autoCaller(this);
2289 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2290
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 HRESULT rc = checkStateDependency(MutableStateDep);
2294 if (FAILED(rc)) return rc;
2295
2296 switch(aId)
2297 {
2298 case 0x0:
2299 case 0x1:
2300 case 0x2:
2301 case 0x3:
2302 case 0x4:
2303 case 0x5:
2304 case 0x6:
2305 case 0x7:
2306 case 0x8:
2307 case 0x9:
2308 case 0xA:
2309 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2310 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2311 setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2314 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2315 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2316 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2317 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2318 break;
2319
2320 case 0x80000000:
2321 case 0x80000001:
2322 case 0x80000002:
2323 case 0x80000003:
2324 case 0x80000004:
2325 case 0x80000005:
2326 case 0x80000006:
2327 case 0x80000007:
2328 case 0x80000008:
2329 case 0x80000009:
2330 case 0x8000000A:
2331 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2332 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2333 setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2336 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2337 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2338 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2339 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2340 break;
2341
2342 default:
2343 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2344 }
2345 return S_OK;
2346}
2347
2348STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2349{
2350 AutoCaller autoCaller(this);
2351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2352
2353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 HRESULT rc = checkStateDependency(MutableStateDep);
2356 if (FAILED(rc)) return rc;
2357
2358 switch(aId)
2359 {
2360 case 0x0:
2361 case 0x1:
2362 case 0x2:
2363 case 0x3:
2364 case 0x4:
2365 case 0x5:
2366 case 0x6:
2367 case 0x7:
2368 case 0x8:
2369 case 0x9:
2370 case 0xA:
2371 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2372 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2373 setModified(IsModified_MachineData);
2374 mHWData.backup();
2375 /* Invalidate leaf. */
2376 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2377 break;
2378
2379 case 0x80000000:
2380 case 0x80000001:
2381 case 0x80000002:
2382 case 0x80000003:
2383 case 0x80000004:
2384 case 0x80000005:
2385 case 0x80000006:
2386 case 0x80000007:
2387 case 0x80000008:
2388 case 0x80000009:
2389 case 0x8000000A:
2390 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2391 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2392 setModified(IsModified_MachineData);
2393 mHWData.backup();
2394 /* Invalidate leaf. */
2395 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2396 break;
2397
2398 default:
2399 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2400 }
2401 return S_OK;
2402}
2403
2404STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2405{
2406 AutoCaller autoCaller(this);
2407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2408
2409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2410
2411 HRESULT rc = checkStateDependency(MutableStateDep);
2412 if (FAILED(rc)) return rc;
2413
2414 setModified(IsModified_MachineData);
2415 mHWData.backup();
2416
2417 /* Invalidate all standard leafs. */
2418 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2419 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2420
2421 /* Invalidate all extended leafs. */
2422 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2423 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2424
2425 return S_OK;
2426}
2427
2428STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2429{
2430 CheckComArgOutPointerValid(aVal);
2431
2432 AutoCaller autoCaller(this);
2433 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2434
2435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2436
2437 switch(property)
2438 {
2439 case HWVirtExPropertyType_Enabled:
2440 *aVal = mHWData->mHWVirtExEnabled;
2441 break;
2442
2443 case HWVirtExPropertyType_Exclusive:
2444 *aVal = mHWData->mHWVirtExExclusive;
2445 break;
2446
2447 case HWVirtExPropertyType_VPID:
2448 *aVal = mHWData->mHWVirtExVPIDEnabled;
2449 break;
2450
2451 case HWVirtExPropertyType_NestedPaging:
2452 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2453 break;
2454
2455 case HWVirtExPropertyType_UnrestrictedExecution:
2456 *aVal = mHWData->mHWVirtExUXEnabled;
2457 break;
2458
2459 case HWVirtExPropertyType_LargePages:
2460 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2461#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2462 *aVal = FALSE;
2463#endif
2464 break;
2465
2466 case HWVirtExPropertyType_Force:
2467 *aVal = mHWData->mHWVirtExForceEnabled;
2468 break;
2469
2470 default:
2471 return E_INVALIDARG;
2472 }
2473 return S_OK;
2474}
2475
2476STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2477{
2478 AutoCaller autoCaller(this);
2479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2480
2481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2482
2483 HRESULT rc = checkStateDependency(MutableStateDep);
2484 if (FAILED(rc)) return rc;
2485
2486 switch(property)
2487 {
2488 case HWVirtExPropertyType_Enabled:
2489 setModified(IsModified_MachineData);
2490 mHWData.backup();
2491 mHWData->mHWVirtExEnabled = !!aVal;
2492 break;
2493
2494 case HWVirtExPropertyType_Exclusive:
2495 setModified(IsModified_MachineData);
2496 mHWData.backup();
2497 mHWData->mHWVirtExExclusive = !!aVal;
2498 break;
2499
2500 case HWVirtExPropertyType_VPID:
2501 setModified(IsModified_MachineData);
2502 mHWData.backup();
2503 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2504 break;
2505
2506 case HWVirtExPropertyType_NestedPaging:
2507 setModified(IsModified_MachineData);
2508 mHWData.backup();
2509 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2510 break;
2511
2512 case HWVirtExPropertyType_UnrestrictedExecution:
2513 setModified(IsModified_MachineData);
2514 mHWData.backup();
2515 mHWData->mHWVirtExUXEnabled = !!aVal;
2516 break;
2517
2518 case HWVirtExPropertyType_LargePages:
2519 setModified(IsModified_MachineData);
2520 mHWData.backup();
2521 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2522 break;
2523
2524 case HWVirtExPropertyType_Force:
2525 setModified(IsModified_MachineData);
2526 mHWData.backup();
2527 mHWData->mHWVirtExForceEnabled = !!aVal;
2528 break;
2529
2530 default:
2531 return E_INVALIDARG;
2532 }
2533
2534 return S_OK;
2535}
2536
2537STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2538{
2539 CheckComArgOutPointerValid(aSnapshotFolder);
2540
2541 AutoCaller autoCaller(this);
2542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2543
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 Utf8Str strFullSnapshotFolder;
2547 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2548 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2549
2550 return S_OK;
2551}
2552
2553STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2554{
2555 /* @todo (r=dmik):
2556 * 1. Allow to change the name of the snapshot folder containing snapshots
2557 * 2. Rename the folder on disk instead of just changing the property
2558 * value (to be smart and not to leave garbage). Note that it cannot be
2559 * done here because the change may be rolled back. Thus, the right
2560 * place is #saveSettings().
2561 */
2562
2563 AutoCaller autoCaller(this);
2564 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2565
2566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 HRESULT rc = checkStateDependency(MutableStateDep);
2569 if (FAILED(rc)) return rc;
2570
2571 if (!mData->mCurrentSnapshot.isNull())
2572 return setError(E_FAIL,
2573 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2574
2575 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2576
2577 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2578 if (strSnapshotFolder.isEmpty())
2579 strSnapshotFolder = "Snapshots";
2580 int vrc = calculateFullPath(strSnapshotFolder,
2581 strSnapshotFolder);
2582 if (RT_FAILURE(vrc))
2583 return setError(E_FAIL,
2584 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2585 aSnapshotFolder, vrc);
2586
2587 setModified(IsModified_MachineData);
2588 mUserData.backup();
2589
2590 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2591
2592 return S_OK;
2593}
2594
2595STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2596{
2597 CheckComArgOutSafeArrayPointerValid(aAttachments);
2598
2599 AutoCaller autoCaller(this);
2600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2601
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2605 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2606
2607 return S_OK;
2608}
2609
2610STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2611{
2612 CheckComArgOutPointerValid(vrdeServer);
2613
2614 AutoCaller autoCaller(this);
2615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2616
2617 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2618
2619 Assert(!!mVRDEServer);
2620 mVRDEServer.queryInterfaceTo(vrdeServer);
2621
2622 return S_OK;
2623}
2624
2625STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2626{
2627 CheckComArgOutPointerValid(audioAdapter);
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 mAudioAdapter.queryInterfaceTo(audioAdapter);
2635 return S_OK;
2636}
2637
2638STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2639{
2640#ifdef VBOX_WITH_VUSB
2641 CheckComArgOutPointerValid(aUSBController);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 clearError();
2647 MultiResult rc(S_OK);
2648
2649# ifdef VBOX_WITH_USB
2650 rc = mParent->host()->checkUSBProxyService();
2651 if (FAILED(rc)) return rc;
2652# endif
2653
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 return rc = mUSBController.queryInterfaceTo(aUSBController);
2657#else
2658 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2659 * extended error info to indicate that USB is simply not available
2660 * (w/o treating it as a failure), for example, as in OSE */
2661 NOREF(aUSBController);
2662 ReturnComNotImplemented();
2663#endif /* VBOX_WITH_VUSB */
2664}
2665
2666STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2667{
2668 CheckComArgOutPointerValid(aFilePath);
2669
2670 AutoLimitedCaller autoCaller(this);
2671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2672
2673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2674
2675 mData->m_strConfigFileFull.cloneTo(aFilePath);
2676 return S_OK;
2677}
2678
2679STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2680{
2681 CheckComArgOutPointerValid(aModified);
2682
2683 AutoCaller autoCaller(this);
2684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2685
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 HRESULT rc = checkStateDependency(MutableStateDep);
2689 if (FAILED(rc)) return rc;
2690
2691 if (!mData->pMachineConfigFile->fileExists())
2692 // this is a new machine, and no config file exists yet:
2693 *aModified = TRUE;
2694 else
2695 *aModified = (mData->flModifications != 0);
2696
2697 return S_OK;
2698}
2699
2700STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2701{
2702 CheckComArgOutPointerValid(aSessionState);
2703
2704 AutoCaller autoCaller(this);
2705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2706
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 *aSessionState = mData->mSession.mState;
2710
2711 return S_OK;
2712}
2713
2714STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2715{
2716 CheckComArgOutPointerValid(aSessionType);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 mData->mSession.mType.cloneTo(aSessionType);
2724
2725 return S_OK;
2726}
2727
2728STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2729{
2730 CheckComArgOutPointerValid(aSessionPID);
2731
2732 AutoCaller autoCaller(this);
2733 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2734
2735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 *aSessionPID = mData->mSession.mPID;
2738
2739 return S_OK;
2740}
2741
2742STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2743{
2744 CheckComArgOutPointerValid(machineState);
2745
2746 AutoCaller autoCaller(this);
2747 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2748
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *machineState = mData->mMachineState;
2752
2753 return S_OK;
2754}
2755
2756STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2757{
2758 CheckComArgOutPointerValid(aLastStateChange);
2759
2760 AutoCaller autoCaller(this);
2761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2762
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2766
2767 return S_OK;
2768}
2769
2770STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2771{
2772 CheckComArgOutPointerValid(aStateFilePath);
2773
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2780
2781 return S_OK;
2782}
2783
2784STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2785{
2786 CheckComArgOutPointerValid(aLogFolder);
2787
2788 AutoCaller autoCaller(this);
2789 AssertComRCReturnRC(autoCaller.rc());
2790
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 Utf8Str logFolder;
2794 getLogFolder(logFolder);
2795 logFolder.cloneTo(aLogFolder);
2796
2797 return S_OK;
2798}
2799
2800STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2801{
2802 CheckComArgOutPointerValid(aCurrentSnapshot);
2803
2804 AutoCaller autoCaller(this);
2805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2806
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2810
2811 return S_OK;
2812}
2813
2814STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2815{
2816 CheckComArgOutPointerValid(aSnapshotCount);
2817
2818 AutoCaller autoCaller(this);
2819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2820
2821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2822
2823 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2824 ? 0
2825 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2826
2827 return S_OK;
2828}
2829
2830STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2831{
2832 CheckComArgOutPointerValid(aCurrentStateModified);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 /* Note: for machines with no snapshots, we always return FALSE
2840 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2841 * reasons :) */
2842
2843 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2844 ? FALSE
2845 : mData->mCurrentStateModified;
2846
2847 return S_OK;
2848}
2849
2850STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2851{
2852 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2853
2854 AutoCaller autoCaller(this);
2855 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2856
2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2858
2859 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2860 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2861
2862 return S_OK;
2863}
2864
2865STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2866{
2867 CheckComArgOutPointerValid(aClipboardMode);
2868
2869 AutoCaller autoCaller(this);
2870 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2871
2872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2873
2874 *aClipboardMode = mHWData->mClipboardMode;
2875
2876 return S_OK;
2877}
2878
2879STDMETHODIMP
2880Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2881{
2882 HRESULT rc = S_OK;
2883
2884 AutoCaller autoCaller(this);
2885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2886
2887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2888
2889 alock.release();
2890 rc = onClipboardModeChange(aClipboardMode);
2891 alock.acquire();
2892 if (FAILED(rc)) return rc;
2893
2894 setModified(IsModified_MachineData);
2895 mHWData.backup();
2896 mHWData->mClipboardMode = aClipboardMode;
2897
2898 /* Save settings if online - todo why is this required?? */
2899 if (Global::IsOnline(mData->mMachineState))
2900 saveSettings(NULL);
2901
2902 return S_OK;
2903}
2904
2905STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2906{
2907 CheckComArgOutPointerValid(aDragAndDropMode);
2908
2909 AutoCaller autoCaller(this);
2910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2911
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 *aDragAndDropMode = mHWData->mDragAndDropMode;
2915
2916 return S_OK;
2917}
2918
2919STDMETHODIMP
2920Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2921{
2922 HRESULT rc = S_OK;
2923
2924 AutoCaller autoCaller(this);
2925 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2926
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 alock.release();
2930 rc = onDragAndDropModeChange(aDragAndDropMode);
2931 alock.acquire();
2932 if (FAILED(rc)) return rc;
2933
2934 setModified(IsModified_MachineData);
2935 mHWData.backup();
2936 mHWData->mDragAndDropMode = aDragAndDropMode;
2937
2938 /* Save settings if online - todo why is this required?? */
2939 if (Global::IsOnline(mData->mMachineState))
2940 saveSettings(NULL);
2941
2942 return S_OK;
2943}
2944
2945STDMETHODIMP
2946Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2947{
2948 CheckComArgOutPointerValid(aPatterns);
2949
2950 AutoCaller autoCaller(this);
2951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2952
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 try
2956 {
2957 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2958 }
2959 catch (...)
2960 {
2961 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2962 }
2963
2964 return S_OK;
2965}
2966
2967STDMETHODIMP
2968Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2969{
2970 AutoCaller autoCaller(this);
2971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2972
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 HRESULT rc = checkStateDependency(MutableStateDep);
2976 if (FAILED(rc)) return rc;
2977
2978 setModified(IsModified_MachineData);
2979 mHWData.backup();
2980 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2981 return rc;
2982}
2983
2984STDMETHODIMP
2985Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2986{
2987 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2988
2989 AutoCaller autoCaller(this);
2990 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2991
2992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2993
2994 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2995 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2996
2997 return S_OK;
2998}
2999
3000STDMETHODIMP
3001Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3002{
3003 CheckComArgOutPointerValid(aEnabled);
3004
3005 AutoCaller autoCaller(this);
3006 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3007
3008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3009
3010 *aEnabled = mUserData->s.fTeleporterEnabled;
3011
3012 return S_OK;
3013}
3014
3015STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3016{
3017 AutoCaller autoCaller(this);
3018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3019
3020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3021
3022 /* Only allow it to be set to true when PoweredOff or Aborted.
3023 (Clearing it is always permitted.) */
3024 if ( aEnabled
3025 && mData->mRegistered
3026 && ( !isSessionMachine()
3027 || ( mData->mMachineState != MachineState_PoweredOff
3028 && mData->mMachineState != MachineState_Teleported
3029 && mData->mMachineState != MachineState_Aborted
3030 )
3031 )
3032 )
3033 return setError(VBOX_E_INVALID_VM_STATE,
3034 tr("The machine is not powered off (state is %s)"),
3035 Global::stringifyMachineState(mData->mMachineState));
3036
3037 setModified(IsModified_MachineData);
3038 mUserData.backup();
3039 mUserData->s.fTeleporterEnabled = !!aEnabled;
3040
3041 return S_OK;
3042}
3043
3044STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3045{
3046 CheckComArgOutPointerValid(aPort);
3047
3048 AutoCaller autoCaller(this);
3049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3050
3051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3052
3053 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3054
3055 return S_OK;
3056}
3057
3058STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3059{
3060 if (aPort >= _64K)
3061 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3062
3063 AutoCaller autoCaller(this);
3064 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3065
3066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3067
3068 HRESULT rc = checkStateDependency(MutableStateDep);
3069 if (FAILED(rc)) return rc;
3070
3071 setModified(IsModified_MachineData);
3072 mUserData.backup();
3073 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3074
3075 return S_OK;
3076}
3077
3078STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3079{
3080 CheckComArgOutPointerValid(aAddress);
3081
3082 AutoCaller autoCaller(this);
3083 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3084
3085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3086
3087 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3088
3089 return S_OK;
3090}
3091
3092STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3093{
3094 AutoCaller autoCaller(this);
3095 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3096
3097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3098
3099 HRESULT rc = checkStateDependency(MutableStateDep);
3100 if (FAILED(rc)) return rc;
3101
3102 setModified(IsModified_MachineData);
3103 mUserData.backup();
3104 mUserData->s.strTeleporterAddress = aAddress;
3105
3106 return S_OK;
3107}
3108
3109STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3110{
3111 CheckComArgOutPointerValid(aPassword);
3112
3113 AutoCaller autoCaller(this);
3114 HRESULT hrc = autoCaller.rc();
3115 if (SUCCEEDED(hrc))
3116 {
3117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3118 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3119 }
3120
3121 return hrc;
3122}
3123
3124STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3125{
3126 /*
3127 * Hash the password first.
3128 */
3129 Utf8Str strPassword(aPassword);
3130 if (!strPassword.isEmpty())
3131 {
3132 if (VBoxIsPasswordHashed(&strPassword))
3133 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3134 VBoxHashPassword(&strPassword);
3135 }
3136
3137 /*
3138 * Do the update.
3139 */
3140 AutoCaller autoCaller(this);
3141 HRESULT hrc = autoCaller.rc();
3142 if (SUCCEEDED(hrc))
3143 {
3144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3145 hrc = checkStateDependency(MutableStateDep);
3146 if (SUCCEEDED(hrc))
3147 {
3148 setModified(IsModified_MachineData);
3149 mUserData.backup();
3150 mUserData->s.strTeleporterPassword = strPassword;
3151 }
3152 }
3153
3154 return hrc;
3155}
3156
3157STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3158{
3159 CheckComArgOutPointerValid(aState);
3160
3161 AutoCaller autoCaller(this);
3162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3163
3164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 *aState = mUserData->s.enmFaultToleranceState;
3167 return S_OK;
3168}
3169
3170STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3171{
3172 AutoCaller autoCaller(this);
3173 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3174
3175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3176
3177 /* @todo deal with running state change. */
3178 HRESULT rc = checkStateDependency(MutableStateDep);
3179 if (FAILED(rc)) return rc;
3180
3181 setModified(IsModified_MachineData);
3182 mUserData.backup();
3183 mUserData->s.enmFaultToleranceState = aState;
3184 return S_OK;
3185}
3186
3187STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3188{
3189 CheckComArgOutPointerValid(aAddress);
3190
3191 AutoCaller autoCaller(this);
3192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3193
3194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3195
3196 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3197 return S_OK;
3198}
3199
3200STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3201{
3202 AutoCaller autoCaller(this);
3203 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3204
3205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3206
3207 /* @todo deal with running state change. */
3208 HRESULT rc = checkStateDependency(MutableStateDep);
3209 if (FAILED(rc)) return rc;
3210
3211 setModified(IsModified_MachineData);
3212 mUserData.backup();
3213 mUserData->s.strFaultToleranceAddress = aAddress;
3214 return S_OK;
3215}
3216
3217STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3218{
3219 CheckComArgOutPointerValid(aPort);
3220
3221 AutoCaller autoCaller(this);
3222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3223
3224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3225
3226 *aPort = mUserData->s.uFaultTolerancePort;
3227 return S_OK;
3228}
3229
3230STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3231{
3232 AutoCaller autoCaller(this);
3233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3234
3235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3236
3237 /* @todo deal with running state change. */
3238 HRESULT rc = checkStateDependency(MutableStateDep);
3239 if (FAILED(rc)) return rc;
3240
3241 setModified(IsModified_MachineData);
3242 mUserData.backup();
3243 mUserData->s.uFaultTolerancePort = aPort;
3244 return S_OK;
3245}
3246
3247STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3248{
3249 CheckComArgOutPointerValid(aPassword);
3250
3251 AutoCaller autoCaller(this);
3252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3253
3254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3255
3256 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3257
3258 return S_OK;
3259}
3260
3261STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3262{
3263 AutoCaller autoCaller(this);
3264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3265
3266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3267
3268 /* @todo deal with running state change. */
3269 HRESULT rc = checkStateDependency(MutableStateDep);
3270 if (FAILED(rc)) return rc;
3271
3272 setModified(IsModified_MachineData);
3273 mUserData.backup();
3274 mUserData->s.strFaultTolerancePassword = aPassword;
3275
3276 return S_OK;
3277}
3278
3279STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3280{
3281 CheckComArgOutPointerValid(aInterval);
3282
3283 AutoCaller autoCaller(this);
3284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3285
3286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3287
3288 *aInterval = mUserData->s.uFaultToleranceInterval;
3289 return S_OK;
3290}
3291
3292STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3293{
3294 AutoCaller autoCaller(this);
3295 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3296
3297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3298
3299 /* @todo deal with running state change. */
3300 HRESULT rc = checkStateDependency(MutableStateDep);
3301 if (FAILED(rc)) return rc;
3302
3303 setModified(IsModified_MachineData);
3304 mUserData.backup();
3305 mUserData->s.uFaultToleranceInterval = aInterval;
3306 return S_OK;
3307}
3308
3309STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3310{
3311 CheckComArgOutPointerValid(aEnabled);
3312
3313 AutoCaller autoCaller(this);
3314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3315
3316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3317
3318 *aEnabled = mUserData->s.fRTCUseUTC;
3319
3320 return S_OK;
3321}
3322
3323STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3324{
3325 AutoCaller autoCaller(this);
3326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3327
3328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3329
3330 /* Only allow it to be set to true when PoweredOff or Aborted.
3331 (Clearing it is always permitted.) */
3332 if ( aEnabled
3333 && mData->mRegistered
3334 && ( !isSessionMachine()
3335 || ( mData->mMachineState != MachineState_PoweredOff
3336 && mData->mMachineState != MachineState_Teleported
3337 && mData->mMachineState != MachineState_Aborted
3338 )
3339 )
3340 )
3341 return setError(VBOX_E_INVALID_VM_STATE,
3342 tr("The machine is not powered off (state is %s)"),
3343 Global::stringifyMachineState(mData->mMachineState));
3344
3345 setModified(IsModified_MachineData);
3346 mUserData.backup();
3347 mUserData->s.fRTCUseUTC = !!aEnabled;
3348
3349 return S_OK;
3350}
3351
3352STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3353{
3354 CheckComArgOutPointerValid(aEnabled);
3355
3356 AutoCaller autoCaller(this);
3357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3358
3359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3360
3361 *aEnabled = mHWData->mIOCacheEnabled;
3362
3363 return S_OK;
3364}
3365
3366STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3367{
3368 AutoCaller autoCaller(this);
3369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3370
3371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3372
3373 HRESULT rc = checkStateDependency(MutableStateDep);
3374 if (FAILED(rc)) return rc;
3375
3376 setModified(IsModified_MachineData);
3377 mHWData.backup();
3378 mHWData->mIOCacheEnabled = aEnabled;
3379
3380 return S_OK;
3381}
3382
3383STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3384{
3385 CheckComArgOutPointerValid(aIOCacheSize);
3386
3387 AutoCaller autoCaller(this);
3388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3389
3390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3391
3392 *aIOCacheSize = mHWData->mIOCacheSize;
3393
3394 return S_OK;
3395}
3396
3397STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3398{
3399 AutoCaller autoCaller(this);
3400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3401
3402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3403
3404 HRESULT rc = checkStateDependency(MutableStateDep);
3405 if (FAILED(rc)) return rc;
3406
3407 setModified(IsModified_MachineData);
3408 mHWData.backup();
3409 mHWData->mIOCacheSize = aIOCacheSize;
3410
3411 return S_OK;
3412}
3413
3414
3415/**
3416 * @note Locks objects!
3417 */
3418STDMETHODIMP Machine::LockMachine(ISession *aSession,
3419 LockType_T lockType)
3420{
3421 CheckComArgNotNull(aSession);
3422
3423 AutoCaller autoCaller(this);
3424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3425
3426 /* check the session state */
3427 SessionState_T state;
3428 HRESULT rc = aSession->COMGETTER(State)(&state);
3429 if (FAILED(rc)) return rc;
3430
3431 if (state != SessionState_Unlocked)
3432 return setError(VBOX_E_INVALID_OBJECT_STATE,
3433 tr("The given session is busy"));
3434
3435 // get the client's IInternalSessionControl interface
3436 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3437 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3438 E_INVALIDARG);
3439
3440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3441
3442 if (!mData->mRegistered)
3443 return setError(E_UNEXPECTED,
3444 tr("The machine '%s' is not registered"),
3445 mUserData->s.strName.c_str());
3446
3447 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3448
3449 SessionState_T oldState = mData->mSession.mState;
3450 /* Hack: in case the session is closing and there is a progress object
3451 * which allows waiting for the session to be closed, take the opportunity
3452 * and do a limited wait (max. 1 second). This helps a lot when the system
3453 * is busy and thus session closing can take a little while. */
3454 if ( mData->mSession.mState == SessionState_Unlocking
3455 && mData->mSession.mProgress)
3456 {
3457 alock.release();
3458 mData->mSession.mProgress->WaitForCompletion(1000);
3459 alock.acquire();
3460 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3461 }
3462
3463 // try again now
3464 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3465 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3466 )
3467 {
3468 // OK, share the session... we are now dealing with three processes:
3469 // 1) VBoxSVC (where this code runs);
3470 // 2) process C: the caller's client process (who wants a shared session);
3471 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3472
3473 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3474 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3475 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3476 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3477 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3478
3479 /*
3480 * Release the lock before calling the client process. It's safe here
3481 * since the only thing to do after we get the lock again is to add
3482 * the remote control to the list (which doesn't directly influence
3483 * anything).
3484 */
3485 alock.release();
3486
3487 // get the console of the session holding the write lock (this is a remote call)
3488 ComPtr<IConsole> pConsoleW;
3489 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3490 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3491 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3492 if (FAILED(rc))
3493 // the failure may occur w/o any error info (from RPC), so provide one
3494 return setError(VBOX_E_VM_ERROR,
3495 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3496
3497 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3498
3499 // share the session machine and W's console with the caller's session
3500 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3501 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3502 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3503
3504 if (FAILED(rc))
3505 // the failure may occur w/o any error info (from RPC), so provide one
3506 return setError(VBOX_E_VM_ERROR,
3507 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3508 alock.acquire();
3509
3510 // need to revalidate the state after acquiring the lock again
3511 if (mData->mSession.mState != SessionState_Locked)
3512 {
3513 pSessionControl->Uninitialize();
3514 return setError(VBOX_E_INVALID_SESSION_STATE,
3515 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3516 mUserData->s.strName.c_str());
3517 }
3518
3519 // add the caller's session to the list
3520 mData->mSession.mRemoteControls.push_back(pSessionControl);
3521 }
3522 else if ( mData->mSession.mState == SessionState_Locked
3523 || mData->mSession.mState == SessionState_Unlocking
3524 )
3525 {
3526 // sharing not permitted, or machine still unlocking:
3527 return setError(VBOX_E_INVALID_OBJECT_STATE,
3528 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3529 mUserData->s.strName.c_str());
3530 }
3531 else
3532 {
3533 // machine is not locked: then write-lock the machine (create the session machine)
3534
3535 // must not be busy
3536 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3537
3538 // get the caller's session PID
3539 RTPROCESS pid = NIL_RTPROCESS;
3540 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3541 pSessionControl->GetPID((ULONG*)&pid);
3542 Assert(pid != NIL_RTPROCESS);
3543
3544 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3545
3546 if (fLaunchingVMProcess)
3547 {
3548 // this machine is awaiting for a spawning session to be opened:
3549 // then the calling process must be the one that got started by
3550 // LaunchVMProcess()
3551
3552 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3553 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3554
3555 if (mData->mSession.mPID != pid)
3556 return setError(E_ACCESSDENIED,
3557 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3558 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3559 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3560 }
3561
3562 // create the mutable SessionMachine from the current machine
3563 ComObjPtr<SessionMachine> sessionMachine;
3564 sessionMachine.createObject();
3565 rc = sessionMachine->init(this);
3566 AssertComRC(rc);
3567
3568 /* NOTE: doing return from this function after this point but
3569 * before the end is forbidden since it may call SessionMachine::uninit()
3570 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3571 * lock while still holding the Machine lock in alock so that a deadlock
3572 * is possible due to the wrong lock order. */
3573
3574 if (SUCCEEDED(rc))
3575 {
3576 /*
3577 * Set the session state to Spawning to protect against subsequent
3578 * attempts to open a session and to unregister the machine after
3579 * we release the lock.
3580 */
3581 SessionState_T origState = mData->mSession.mState;
3582 mData->mSession.mState = SessionState_Spawning;
3583
3584 /*
3585 * Release the lock before calling the client process -- it will call
3586 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3587 * because the state is Spawning, so that LaunchVMProcess() and
3588 * LockMachine() calls will fail. This method, called before we
3589 * acquire the lock again, will fail because of the wrong PID.
3590 *
3591 * Note that mData->mSession.mRemoteControls accessed outside
3592 * the lock may not be modified when state is Spawning, so it's safe.
3593 */
3594 alock.release();
3595
3596 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3597 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3598 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3599
3600 /* The failure may occur w/o any error info (from RPC), so provide one */
3601 if (FAILED(rc))
3602 setError(VBOX_E_VM_ERROR,
3603 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3604
3605 if ( SUCCEEDED(rc)
3606 && fLaunchingVMProcess
3607 )
3608 {
3609 /* complete the remote session initialization */
3610
3611 /* get the console from the direct session */
3612 ComPtr<IConsole> console;
3613 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3614 ComAssertComRC(rc);
3615
3616 if (SUCCEEDED(rc) && !console)
3617 {
3618 ComAssert(!!console);
3619 rc = E_FAIL;
3620 }
3621
3622 /* assign machine & console to the remote session */
3623 if (SUCCEEDED(rc))
3624 {
3625 /*
3626 * after LaunchVMProcess(), the first and the only
3627 * entry in remoteControls is that remote session
3628 */
3629 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3630 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3631 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3632
3633 /* The failure may occur w/o any error info (from RPC), so provide one */
3634 if (FAILED(rc))
3635 setError(VBOX_E_VM_ERROR,
3636 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3637 }
3638
3639 if (FAILED(rc))
3640 pSessionControl->Uninitialize();
3641 }
3642
3643 /* acquire the lock again */
3644 alock.acquire();
3645
3646 /* Restore the session state */
3647 mData->mSession.mState = origState;
3648 }
3649
3650 // finalize spawning anyway (this is why we don't return on errors above)
3651 if (fLaunchingVMProcess)
3652 {
3653 /* Note that the progress object is finalized later */
3654 /** @todo Consider checking mData->mSession.mProgress for cancellation
3655 * around here. */
3656
3657 /* We don't reset mSession.mPID here because it is necessary for
3658 * SessionMachine::uninit() to reap the child process later. */
3659
3660 if (FAILED(rc))
3661 {
3662 /* Close the remote session, remove the remote control from the list
3663 * and reset session state to Closed (@note keep the code in sync
3664 * with the relevant part in openSession()). */
3665
3666 Assert(mData->mSession.mRemoteControls.size() == 1);
3667 if (mData->mSession.mRemoteControls.size() == 1)
3668 {
3669 ErrorInfoKeeper eik;
3670 mData->mSession.mRemoteControls.front()->Uninitialize();
3671 }
3672
3673 mData->mSession.mRemoteControls.clear();
3674 mData->mSession.mState = SessionState_Unlocked;
3675 }
3676 }
3677 else
3678 {
3679 /* memorize PID of the directly opened session */
3680 if (SUCCEEDED(rc))
3681 mData->mSession.mPID = pid;
3682 }
3683
3684 if (SUCCEEDED(rc))
3685 {
3686 /* memorize the direct session control and cache IUnknown for it */
3687 mData->mSession.mDirectControl = pSessionControl;
3688 mData->mSession.mState = SessionState_Locked;
3689 /* associate the SessionMachine with this Machine */
3690 mData->mSession.mMachine = sessionMachine;
3691
3692 /* request an IUnknown pointer early from the remote party for later
3693 * identity checks (it will be internally cached within mDirectControl
3694 * at least on XPCOM) */
3695 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3696 NOREF(unk);
3697 }
3698
3699 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3700 * would break the lock order */
3701 alock.release();
3702
3703 /* uninitialize the created session machine on failure */
3704 if (FAILED(rc))
3705 sessionMachine->uninit();
3706
3707 }
3708
3709 if (SUCCEEDED(rc))
3710 {
3711 /*
3712 * tell the client watcher thread to update the set of
3713 * machines that have open sessions
3714 */
3715 mParent->updateClientWatcher();
3716
3717 if (oldState != SessionState_Locked)
3718 /* fire an event */
3719 mParent->onSessionStateChange(getId(), SessionState_Locked);
3720 }
3721
3722 return rc;
3723}
3724
3725/**
3726 * @note Locks objects!
3727 */
3728STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3729 IN_BSTR aFrontend,
3730 IN_BSTR aEnvironment,
3731 IProgress **aProgress)
3732{
3733 CheckComArgStr(aFrontend);
3734 Utf8Str strFrontend(aFrontend);
3735 Utf8Str strEnvironment(aEnvironment);
3736 /* "emergencystop" doesn't need the session, so skip the checks/interface
3737 * retrieval. This code doesn't quite fit in here, but introducing a
3738 * special API method would be even more effort, and would require explicit
3739 * support by every API client. It's better to hide the feature a bit. */
3740 if (strFrontend != "emergencystop")
3741 CheckComArgNotNull(aSession);
3742 CheckComArgOutPointerValid(aProgress);
3743
3744 AutoCaller autoCaller(this);
3745 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3746
3747 HRESULT rc = S_OK;
3748 if (strFrontend.isEmpty())
3749 {
3750 Bstr bstrFrontend;
3751 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3752 if (FAILED(rc))
3753 return rc;
3754 strFrontend = bstrFrontend;
3755 if (strFrontend.isEmpty())
3756 {
3757 ComPtr<ISystemProperties> systemProperties;
3758 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3759 if (FAILED(rc))
3760 return rc;
3761 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3762 if (FAILED(rc))
3763 return rc;
3764 strFrontend = bstrFrontend;
3765 }
3766 /* paranoia - emergencystop is not a valid default */
3767 if (strFrontend == "emergencystop")
3768 strFrontend = Utf8Str::Empty;
3769 }
3770 /* default frontend: Qt GUI */
3771 if (strFrontend.isEmpty())
3772 strFrontend = "GUI/Qt";
3773
3774 if (strFrontend != "emergencystop")
3775 {
3776 /* check the session state */
3777 SessionState_T state;
3778 rc = aSession->COMGETTER(State)(&state);
3779 if (FAILED(rc))
3780 return rc;
3781
3782 if (state != SessionState_Unlocked)
3783 return setError(VBOX_E_INVALID_OBJECT_STATE,
3784 tr("The given session is busy"));
3785
3786 /* get the IInternalSessionControl interface */
3787 ComPtr<IInternalSessionControl> control(aSession);
3788 ComAssertMsgRet(!control.isNull(),
3789 ("No IInternalSessionControl interface"),
3790 E_INVALIDARG);
3791
3792 /* get the teleporter enable state for the progress object init. */
3793 BOOL fTeleporterEnabled;
3794 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3795 if (FAILED(rc))
3796 return rc;
3797
3798 /* create a progress object */
3799 ComObjPtr<ProgressProxy> progress;
3800 progress.createObject();
3801 rc = progress->init(mParent,
3802 static_cast<IMachine*>(this),
3803 Bstr(tr("Starting VM")).raw(),
3804 TRUE /* aCancelable */,
3805 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3806 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3807 2 /* uFirstOperationWeight */,
3808 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3809
3810 if (SUCCEEDED(rc))
3811 {
3812 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3813 if (SUCCEEDED(rc))
3814 {
3815 progress.queryInterfaceTo(aProgress);
3816
3817 /* signal the client watcher thread */
3818 mParent->updateClientWatcher();
3819
3820 /* fire an event */
3821 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3822 }
3823 }
3824 }
3825 else
3826 {
3827 /* no progress object - either instant success or failure */
3828 *aProgress = NULL;
3829
3830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3831
3832 if (mData->mSession.mState != SessionState_Locked)
3833 return setError(VBOX_E_INVALID_OBJECT_STATE,
3834 tr("The machine '%s' is not locked by a session"),
3835 mUserData->s.strName.c_str());
3836
3837 /* must have a VM process associated - do not kill normal API clients
3838 * with an open session */
3839 if (!Global::IsOnline(mData->mMachineState))
3840 return setError(VBOX_E_INVALID_OBJECT_STATE,
3841 tr("The machine '%s' does not have a VM process"),
3842 mUserData->s.strName.c_str());
3843
3844 /* forcibly terminate the VM process */
3845 if (mData->mSession.mPID != NIL_RTPROCESS)
3846 RTProcTerminate(mData->mSession.mPID);
3847
3848 /* signal the client watcher thread, as most likely the client has
3849 * been terminated */
3850 mParent->updateClientWatcher();
3851 }
3852
3853 return rc;
3854}
3855
3856STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3857{
3858 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3859 return setError(E_INVALIDARG,
3860 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3861 aPosition, SchemaDefs::MaxBootPosition);
3862
3863 if (aDevice == DeviceType_USB)
3864 return setError(E_NOTIMPL,
3865 tr("Booting from USB device is currently not supported"));
3866
3867 AutoCaller autoCaller(this);
3868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3869
3870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3871
3872 HRESULT rc = checkStateDependency(MutableStateDep);
3873 if (FAILED(rc)) return rc;
3874
3875 setModified(IsModified_MachineData);
3876 mHWData.backup();
3877 mHWData->mBootOrder[aPosition - 1] = aDevice;
3878
3879 return S_OK;
3880}
3881
3882STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3883{
3884 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3885 return setError(E_INVALIDARG,
3886 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3887 aPosition, SchemaDefs::MaxBootPosition);
3888
3889 AutoCaller autoCaller(this);
3890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3891
3892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3893
3894 *aDevice = mHWData->mBootOrder[aPosition - 1];
3895
3896 return S_OK;
3897}
3898
3899STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3900 LONG aControllerPort,
3901 LONG aDevice,
3902 DeviceType_T aType,
3903 IMedium *aMedium)
3904{
3905 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3906 aControllerName, aControllerPort, aDevice, aType, aMedium));
3907
3908 CheckComArgStrNotEmptyOrNull(aControllerName);
3909
3910 AutoCaller autoCaller(this);
3911 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3912
3913 // request the host lock first, since might be calling Host methods for getting host drives;
3914 // next, protect the media tree all the while we're in here, as well as our member variables
3915 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3916 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3917
3918 HRESULT rc = checkStateDependency(MutableStateDep);
3919 if (FAILED(rc)) return rc;
3920
3921 /// @todo NEWMEDIA implicit machine registration
3922 if (!mData->mRegistered)
3923 return setError(VBOX_E_INVALID_OBJECT_STATE,
3924 tr("Cannot attach storage devices to an unregistered machine"));
3925
3926 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3927
3928 /* Check for an existing controller. */
3929 ComObjPtr<StorageController> ctl;
3930 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3931 if (FAILED(rc)) return rc;
3932
3933 StorageControllerType_T ctrlType;
3934 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3935 if (FAILED(rc))
3936 return setError(E_FAIL,
3937 tr("Could not get type of controller '%ls'"),
3938 aControllerName);
3939
3940 bool fSilent = false;
3941 Utf8Str strReconfig;
3942
3943 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3944 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3945 if (FAILED(rc))
3946 return rc;
3947 if ( mData->mMachineState == MachineState_Paused
3948 && strReconfig == "1")
3949 fSilent = true;
3950
3951 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3952 bool fHotplug = false;
3953 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3954 fHotplug = true;
3955
3956 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3957 return setError(VBOX_E_INVALID_VM_STATE,
3958 tr("Controller '%ls' does not support hotplugging"),
3959 aControllerName);
3960
3961 // check that the port and device are not out of range
3962 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3963 if (FAILED(rc)) return rc;
3964
3965 /* check if the device slot is already busy */
3966 MediumAttachment *pAttachTemp;
3967 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3968 aControllerName,
3969 aControllerPort,
3970 aDevice)))
3971 {
3972 Medium *pMedium = pAttachTemp->getMedium();
3973 if (pMedium)
3974 {
3975 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3976 return setError(VBOX_E_OBJECT_IN_USE,
3977 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3978 pMedium->getLocationFull().c_str(),
3979 aControllerPort,
3980 aDevice,
3981 aControllerName);
3982 }
3983 else
3984 return setError(VBOX_E_OBJECT_IN_USE,
3985 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3986 aControllerPort, aDevice, aControllerName);
3987 }
3988
3989 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3990 if (aMedium && medium.isNull())
3991 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3992
3993 AutoCaller mediumCaller(medium);
3994 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3995
3996 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3997
3998 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3999 && !medium.isNull()
4000 )
4001 return setError(VBOX_E_OBJECT_IN_USE,
4002 tr("Medium '%s' is already attached to this virtual machine"),
4003 medium->getLocationFull().c_str());
4004
4005 if (!medium.isNull())
4006 {
4007 MediumType_T mtype = medium->getType();
4008 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4009 // For DVDs it's not written to the config file, so needs no global config
4010 // version bump. For floppies it's a new attribute "type", which is ignored
4011 // by older VirtualBox version, so needs no global config version bump either.
4012 // For hard disks this type is not accepted.
4013 if (mtype == MediumType_MultiAttach)
4014 {
4015 // This type is new with VirtualBox 4.0 and therefore requires settings
4016 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4017 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4018 // two reasons: The medium type is a property of the media registry tree, which
4019 // can reside in the global config file (for pre-4.0 media); we would therefore
4020 // possibly need to bump the global config version. We don't want to do that though
4021 // because that might make downgrading to pre-4.0 impossible.
4022 // As a result, we can only use these two new types if the medium is NOT in the
4023 // global registry:
4024 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4025 if ( medium->isInRegistry(uuidGlobalRegistry)
4026 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4027 )
4028 return setError(VBOX_E_INVALID_OBJECT_STATE,
4029 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4030 "to machines that were created with VirtualBox 4.0 or later"),
4031 medium->getLocationFull().c_str());
4032 }
4033 }
4034
4035 bool fIndirect = false;
4036 if (!medium.isNull())
4037 fIndirect = medium->isReadOnly();
4038 bool associate = true;
4039
4040 do
4041 {
4042 if ( aType == DeviceType_HardDisk
4043 && mMediaData.isBackedUp())
4044 {
4045 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4046
4047 /* check if the medium was attached to the VM before we started
4048 * changing attachments in which case the attachment just needs to
4049 * be restored */
4050 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4051 {
4052 AssertReturn(!fIndirect, E_FAIL);
4053
4054 /* see if it's the same bus/channel/device */
4055 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4056 {
4057 /* the simplest case: restore the whole attachment
4058 * and return, nothing else to do */
4059 mMediaData->mAttachments.push_back(pAttachTemp);
4060
4061 /* Reattach the medium to the VM. */
4062 if (fHotplug || fSilent)
4063 {
4064 mediumLock.release();
4065 treeLock.release();
4066 alock.release();
4067
4068 MediumLockList *pMediumLockList(new MediumLockList());
4069
4070 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4071 true /* fMediumLockWrite */,
4072 NULL,
4073 *pMediumLockList);
4074 alock.acquire();
4075 if (FAILED(rc))
4076 delete pMediumLockList;
4077 else
4078 {
4079 mData->mSession.mLockedMedia.Unlock();
4080 alock.release();
4081 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4082 mData->mSession.mLockedMedia.Lock();
4083 alock.acquire();
4084 }
4085 alock.release();
4086
4087 if (SUCCEEDED(rc))
4088 {
4089 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4090 /* Remove lock list in case of error. */
4091 if (FAILED(rc))
4092 {
4093 mData->mSession.mLockedMedia.Unlock();
4094 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4095 mData->mSession.mLockedMedia.Lock();
4096 }
4097 }
4098 }
4099
4100 return S_OK;
4101 }
4102
4103 /* bus/channel/device differ; we need a new attachment object,
4104 * but don't try to associate it again */
4105 associate = false;
4106 break;
4107 }
4108 }
4109
4110 /* go further only if the attachment is to be indirect */
4111 if (!fIndirect)
4112 break;
4113
4114 /* perform the so called smart attachment logic for indirect
4115 * attachments. Note that smart attachment is only applicable to base
4116 * hard disks. */
4117
4118 if (medium->getParent().isNull())
4119 {
4120 /* first, investigate the backup copy of the current hard disk
4121 * attachments to make it possible to re-attach existing diffs to
4122 * another device slot w/o losing their contents */
4123 if (mMediaData.isBackedUp())
4124 {
4125 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4126
4127 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4128 uint32_t foundLevel = 0;
4129
4130 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4131 it != oldAtts.end();
4132 ++it)
4133 {
4134 uint32_t level = 0;
4135 MediumAttachment *pAttach = *it;
4136 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4137 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4138 if (pMedium.isNull())
4139 continue;
4140
4141 if (pMedium->getBase(&level) == medium)
4142 {
4143 /* skip the hard disk if its currently attached (we
4144 * cannot attach the same hard disk twice) */
4145 if (findAttachment(mMediaData->mAttachments,
4146 pMedium))
4147 continue;
4148
4149 /* matched device, channel and bus (i.e. attached to the
4150 * same place) will win and immediately stop the search;
4151 * otherwise the attachment that has the youngest
4152 * descendant of medium will be used
4153 */
4154 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4155 {
4156 /* the simplest case: restore the whole attachment
4157 * and return, nothing else to do */
4158 mMediaData->mAttachments.push_back(*it);
4159
4160 /* Reattach the medium to the VM. */
4161 if (fHotplug || fSilent)
4162 {
4163 mediumLock.release();
4164 treeLock.release();
4165 alock.release();
4166
4167 MediumLockList *pMediumLockList(new MediumLockList());
4168
4169 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4170 true /* fMediumLockWrite */,
4171 NULL,
4172 *pMediumLockList);
4173 alock.acquire();
4174 if (FAILED(rc))
4175 delete pMediumLockList;
4176 else
4177 {
4178 mData->mSession.mLockedMedia.Unlock();
4179 alock.release();
4180 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4181 mData->mSession.mLockedMedia.Lock();
4182 alock.acquire();
4183 }
4184 alock.release();
4185
4186 if (SUCCEEDED(rc))
4187 {
4188 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4189 /* Remove lock list in case of error. */
4190 if (FAILED(rc))
4191 {
4192 mData->mSession.mLockedMedia.Unlock();
4193 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4194 mData->mSession.mLockedMedia.Lock();
4195 }
4196 }
4197 }
4198
4199 return S_OK;
4200 }
4201 else if ( foundIt == oldAtts.end()
4202 || level > foundLevel /* prefer younger */
4203 )
4204 {
4205 foundIt = it;
4206 foundLevel = level;
4207 }
4208 }
4209 }
4210
4211 if (foundIt != oldAtts.end())
4212 {
4213 /* use the previously attached hard disk */
4214 medium = (*foundIt)->getMedium();
4215 mediumCaller.attach(medium);
4216 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4217 mediumLock.attach(medium);
4218 /* not implicit, doesn't require association with this VM */
4219 fIndirect = false;
4220 associate = false;
4221 /* go right to the MediumAttachment creation */
4222 break;
4223 }
4224 }
4225
4226 /* must give up the medium lock and medium tree lock as below we
4227 * go over snapshots, which needs a lock with higher lock order. */
4228 mediumLock.release();
4229 treeLock.release();
4230
4231 /* then, search through snapshots for the best diff in the given
4232 * hard disk's chain to base the new diff on */
4233
4234 ComObjPtr<Medium> base;
4235 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4236 while (snap)
4237 {
4238 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4239
4240 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4241
4242 MediumAttachment *pAttachFound = NULL;
4243 uint32_t foundLevel = 0;
4244
4245 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4246 it != snapAtts.end();
4247 ++it)
4248 {
4249 MediumAttachment *pAttach = *it;
4250 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4251 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4252 if (pMedium.isNull())
4253 continue;
4254
4255 uint32_t level = 0;
4256 if (pMedium->getBase(&level) == medium)
4257 {
4258 /* matched device, channel and bus (i.e. attached to the
4259 * same place) will win and immediately stop the search;
4260 * otherwise the attachment that has the youngest
4261 * descendant of medium will be used
4262 */
4263 if ( pAttach->getDevice() == aDevice
4264 && pAttach->getPort() == aControllerPort
4265 && pAttach->getControllerName() == aControllerName
4266 )
4267 {
4268 pAttachFound = pAttach;
4269 break;
4270 }
4271 else if ( !pAttachFound
4272 || level > foundLevel /* prefer younger */
4273 )
4274 {
4275 pAttachFound = pAttach;
4276 foundLevel = level;
4277 }
4278 }
4279 }
4280
4281 if (pAttachFound)
4282 {
4283 base = pAttachFound->getMedium();
4284 break;
4285 }
4286
4287 snap = snap->getParent();
4288 }
4289
4290 /* re-lock medium tree and the medium, as we need it below */
4291 treeLock.acquire();
4292 mediumLock.acquire();
4293
4294 /* found a suitable diff, use it as a base */
4295 if (!base.isNull())
4296 {
4297 medium = base;
4298 mediumCaller.attach(medium);
4299 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4300 mediumLock.attach(medium);
4301 }
4302 }
4303
4304 Utf8Str strFullSnapshotFolder;
4305 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4306
4307 ComObjPtr<Medium> diff;
4308 diff.createObject();
4309 // store this diff in the same registry as the parent
4310 Guid uuidRegistryParent;
4311 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4312 {
4313 // parent image has no registry: this can happen if we're attaching a new immutable
4314 // image that has not yet been attached (medium then points to the base and we're
4315 // creating the diff image for the immutable, and the parent is not yet registered);
4316 // put the parent in the machine registry then
4317 mediumLock.release();
4318 treeLock.release();
4319 alock.release();
4320 addMediumToRegistry(medium);
4321 alock.acquire();
4322 treeLock.acquire();
4323 mediumLock.acquire();
4324 medium->getFirstRegistryMachineId(uuidRegistryParent);
4325 }
4326 rc = diff->init(mParent,
4327 medium->getPreferredDiffFormat(),
4328 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4329 uuidRegistryParent);
4330 if (FAILED(rc)) return rc;
4331
4332 /* Apply the normal locking logic to the entire chain. */
4333 MediumLockList *pMediumLockList(new MediumLockList());
4334 mediumLock.release();
4335 treeLock.release();
4336 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4337 true /* fMediumLockWrite */,
4338 medium,
4339 *pMediumLockList);
4340 treeLock.acquire();
4341 mediumLock.acquire();
4342 if (SUCCEEDED(rc))
4343 {
4344 mediumLock.release();
4345 treeLock.release();
4346 rc = pMediumLockList->Lock();
4347 treeLock.acquire();
4348 mediumLock.acquire();
4349 if (FAILED(rc))
4350 setError(rc,
4351 tr("Could not lock medium when creating diff '%s'"),
4352 diff->getLocationFull().c_str());
4353 else
4354 {
4355 /* will release the lock before the potentially lengthy
4356 * operation, so protect with the special state */
4357 MachineState_T oldState = mData->mMachineState;
4358 setMachineState(MachineState_SettingUp);
4359
4360 mediumLock.release();
4361 treeLock.release();
4362 alock.release();
4363
4364 rc = medium->createDiffStorage(diff,
4365 MediumVariant_Standard,
4366 pMediumLockList,
4367 NULL /* aProgress */,
4368 true /* aWait */);
4369
4370 alock.acquire();
4371 treeLock.acquire();
4372 mediumLock.acquire();
4373
4374 setMachineState(oldState);
4375 }
4376 }
4377
4378 /* Unlock the media and free the associated memory. */
4379 delete pMediumLockList;
4380
4381 if (FAILED(rc)) return rc;
4382
4383 /* use the created diff for the actual attachment */
4384 medium = diff;
4385 mediumCaller.attach(medium);
4386 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4387 mediumLock.attach(medium);
4388 }
4389 while (0);
4390
4391 ComObjPtr<MediumAttachment> attachment;
4392 attachment.createObject();
4393 rc = attachment->init(this,
4394 medium,
4395 aControllerName,
4396 aControllerPort,
4397 aDevice,
4398 aType,
4399 fIndirect,
4400 false /* fPassthrough */,
4401 false /* fTempEject */,
4402 false /* fNonRotational */,
4403 false /* fDiscard */,
4404 Utf8Str::Empty);
4405 if (FAILED(rc)) return rc;
4406
4407 if (associate && !medium.isNull())
4408 {
4409 // as the last step, associate the medium to the VM
4410 rc = medium->addBackReference(mData->mUuid);
4411 // here we can fail because of Deleting, or being in process of creating a Diff
4412 if (FAILED(rc)) return rc;
4413
4414 mediumLock.release();
4415 treeLock.release();
4416 alock.release();
4417 addMediumToRegistry(medium);
4418 alock.acquire();
4419 treeLock.acquire();
4420 mediumLock.acquire();
4421 }
4422
4423 /* success: finally remember the attachment */
4424 setModified(IsModified_Storage);
4425 mMediaData.backup();
4426 mMediaData->mAttachments.push_back(attachment);
4427
4428 mediumLock.release();
4429 treeLock.release();
4430 alock.release();
4431
4432 if (fHotplug || fSilent)
4433 {
4434 MediumLockList *pMediumLockList(new MediumLockList());
4435
4436 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4437 true /* fMediumLockWrite */,
4438 NULL,
4439 *pMediumLockList);
4440 alock.acquire();
4441 if (FAILED(rc))
4442 delete pMediumLockList;
4443 else
4444 {
4445 mData->mSession.mLockedMedia.Unlock();
4446 alock.release();
4447 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4448 mData->mSession.mLockedMedia.Lock();
4449 alock.acquire();
4450 }
4451 alock.release();
4452
4453 if (SUCCEEDED(rc))
4454 {
4455 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4456 /* Remove lock list in case of error. */
4457 if (FAILED(rc))
4458 {
4459 mData->mSession.mLockedMedia.Unlock();
4460 mData->mSession.mLockedMedia.Remove(attachment);
4461 mData->mSession.mLockedMedia.Lock();
4462 }
4463 }
4464 }
4465
4466 mParent->saveModifiedRegistries();
4467
4468 return rc;
4469}
4470
4471STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4472 LONG aDevice)
4473{
4474 CheckComArgStrNotEmptyOrNull(aControllerName);
4475
4476 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4477 aControllerName, aControllerPort, aDevice));
4478
4479 AutoCaller autoCaller(this);
4480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4481
4482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4483
4484 HRESULT rc = checkStateDependency(MutableStateDep);
4485 if (FAILED(rc)) return rc;
4486
4487 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4488
4489 /* Check for an existing controller. */
4490 ComObjPtr<StorageController> ctl;
4491 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4492 if (FAILED(rc)) return rc;
4493
4494 StorageControllerType_T ctrlType;
4495 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4496 if (FAILED(rc))
4497 return setError(E_FAIL,
4498 tr("Could not get type of controller '%ls'"),
4499 aControllerName);
4500
4501 bool fSilent = false;
4502 Utf8Str strReconfig;
4503
4504 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4505 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4506 if (FAILED(rc))
4507 return rc;
4508 if ( mData->mMachineState == MachineState_Paused
4509 && strReconfig == "1")
4510 fSilent = true;
4511
4512 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4513 bool fHotplug = false;
4514 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4515 fHotplug = true;
4516
4517 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4518 return setError(VBOX_E_INVALID_VM_STATE,
4519 tr("Controller '%ls' does not support hotplugging"),
4520 aControllerName);
4521
4522 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4523 aControllerName,
4524 aControllerPort,
4525 aDevice);
4526 if (!pAttach)
4527 return setError(VBOX_E_OBJECT_NOT_FOUND,
4528 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4529 aDevice, aControllerPort, aControllerName);
4530
4531 /*
4532 * The VM has to detach the device before we delete any implicit diffs.
4533 * If this fails we can roll back without loosing data.
4534 */
4535 if (fHotplug || fSilent)
4536 {
4537 alock.release();
4538 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4539 alock.acquire();
4540 }
4541 if (FAILED(rc)) return rc;
4542
4543 /* If we are here everything went well and we can delete the implicit now. */
4544 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4545
4546 alock.release();
4547
4548 mParent->saveModifiedRegistries();
4549
4550 return rc;
4551}
4552
4553STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4554 LONG aDevice, BOOL aPassthrough)
4555{
4556 CheckComArgStrNotEmptyOrNull(aControllerName);
4557
4558 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4559 aControllerName, aControllerPort, aDevice, aPassthrough));
4560
4561 AutoCaller autoCaller(this);
4562 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4563
4564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4565
4566 HRESULT rc = checkStateDependency(MutableStateDep);
4567 if (FAILED(rc)) return rc;
4568
4569 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4570
4571 if (Global::IsOnlineOrTransient(mData->mMachineState))
4572 return setError(VBOX_E_INVALID_VM_STATE,
4573 tr("Invalid machine state: %s"),
4574 Global::stringifyMachineState(mData->mMachineState));
4575
4576 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4577 aControllerName,
4578 aControllerPort,
4579 aDevice);
4580 if (!pAttach)
4581 return setError(VBOX_E_OBJECT_NOT_FOUND,
4582 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4583 aDevice, aControllerPort, aControllerName);
4584
4585
4586 setModified(IsModified_Storage);
4587 mMediaData.backup();
4588
4589 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4590
4591 if (pAttach->getType() != DeviceType_DVD)
4592 return setError(E_INVALIDARG,
4593 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4594 aDevice, aControllerPort, aControllerName);
4595 pAttach->updatePassthrough(!!aPassthrough);
4596
4597 return S_OK;
4598}
4599
4600STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4601 LONG aDevice, BOOL aTemporaryEject)
4602{
4603 CheckComArgStrNotEmptyOrNull(aControllerName);
4604
4605 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4606 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4607
4608 AutoCaller autoCaller(this);
4609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4610
4611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4612
4613 HRESULT rc = checkStateDependency(MutableStateDep);
4614 if (FAILED(rc)) return rc;
4615
4616 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4617 aControllerName,
4618 aControllerPort,
4619 aDevice);
4620 if (!pAttach)
4621 return setError(VBOX_E_OBJECT_NOT_FOUND,
4622 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4623 aDevice, aControllerPort, aControllerName);
4624
4625
4626 setModified(IsModified_Storage);
4627 mMediaData.backup();
4628
4629 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4630
4631 if (pAttach->getType() != DeviceType_DVD)
4632 return setError(E_INVALIDARG,
4633 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4634 aDevice, aControllerPort, aControllerName);
4635 pAttach->updateTempEject(!!aTemporaryEject);
4636
4637 return S_OK;
4638}
4639
4640STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4641 LONG aDevice, BOOL aNonRotational)
4642{
4643 CheckComArgStrNotEmptyOrNull(aControllerName);
4644
4645 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4646 aControllerName, aControllerPort, aDevice, aNonRotational));
4647
4648 AutoCaller autoCaller(this);
4649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4650
4651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4652
4653 HRESULT rc = checkStateDependency(MutableStateDep);
4654 if (FAILED(rc)) return rc;
4655
4656 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4657
4658 if (Global::IsOnlineOrTransient(mData->mMachineState))
4659 return setError(VBOX_E_INVALID_VM_STATE,
4660 tr("Invalid machine state: %s"),
4661 Global::stringifyMachineState(mData->mMachineState));
4662
4663 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4664 aControllerName,
4665 aControllerPort,
4666 aDevice);
4667 if (!pAttach)
4668 return setError(VBOX_E_OBJECT_NOT_FOUND,
4669 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4670 aDevice, aControllerPort, aControllerName);
4671
4672
4673 setModified(IsModified_Storage);
4674 mMediaData.backup();
4675
4676 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4677
4678 if (pAttach->getType() != DeviceType_HardDisk)
4679 return setError(E_INVALIDARG,
4680 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"),
4681 aDevice, aControllerPort, aControllerName);
4682 pAttach->updateNonRotational(!!aNonRotational);
4683
4684 return S_OK;
4685}
4686
4687STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4688 LONG aDevice, BOOL aDiscard)
4689{
4690 CheckComArgStrNotEmptyOrNull(aControllerName);
4691
4692 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4693 aControllerName, aControllerPort, aDevice, aDiscard));
4694
4695 AutoCaller autoCaller(this);
4696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4697
4698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4699
4700 HRESULT rc = checkStateDependency(MutableStateDep);
4701 if (FAILED(rc)) return rc;
4702
4703 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4704
4705 if (Global::IsOnlineOrTransient(mData->mMachineState))
4706 return setError(VBOX_E_INVALID_VM_STATE,
4707 tr("Invalid machine state: %s"),
4708 Global::stringifyMachineState(mData->mMachineState));
4709
4710 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4711 aControllerName,
4712 aControllerPort,
4713 aDevice);
4714 if (!pAttach)
4715 return setError(VBOX_E_OBJECT_NOT_FOUND,
4716 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4717 aDevice, aControllerPort, aControllerName);
4718
4719
4720 setModified(IsModified_Storage);
4721 mMediaData.backup();
4722
4723 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4724
4725 if (pAttach->getType() != DeviceType_HardDisk)
4726 return setError(E_INVALIDARG,
4727 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"),
4728 aDevice, aControllerPort, aControllerName);
4729 pAttach->updateDiscard(!!aDiscard);
4730
4731 return S_OK;
4732}
4733
4734STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4735 LONG aDevice)
4736{
4737 int rc = S_OK;
4738 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4739 aControllerName, aControllerPort, aDevice));
4740
4741 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4742
4743 return rc;
4744}
4745
4746STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4747 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4748{
4749 CheckComArgStrNotEmptyOrNull(aControllerName);
4750
4751 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4752 aControllerName, aControllerPort, aDevice));
4753
4754 AutoCaller autoCaller(this);
4755 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4756
4757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4758
4759 HRESULT rc = checkStateDependency(MutableStateDep);
4760 if (FAILED(rc)) return rc;
4761
4762 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4763
4764 if (Global::IsOnlineOrTransient(mData->mMachineState))
4765 return setError(VBOX_E_INVALID_VM_STATE,
4766 tr("Invalid machine state: %s"),
4767 Global::stringifyMachineState(mData->mMachineState));
4768
4769 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4770 aControllerName,
4771 aControllerPort,
4772 aDevice);
4773 if (!pAttach)
4774 return setError(VBOX_E_OBJECT_NOT_FOUND,
4775 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4776 aDevice, aControllerPort, aControllerName);
4777
4778
4779 setModified(IsModified_Storage);
4780 mMediaData.backup();
4781
4782 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4783 if (aBandwidthGroup && group.isNull())
4784 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4785
4786 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4787
4788 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4789 if (strBandwidthGroupOld.isNotEmpty())
4790 {
4791 /* Get the bandwidth group object and release it - this must not fail. */
4792 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4793 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4794 Assert(SUCCEEDED(rc));
4795
4796 pBandwidthGroupOld->release();
4797 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4798 }
4799
4800 if (!group.isNull())
4801 {
4802 group->reference();
4803 pAttach->updateBandwidthGroup(group->getName());
4804 }
4805
4806 return S_OK;
4807}
4808
4809STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4810 LONG aControllerPort,
4811 LONG aDevice,
4812 DeviceType_T aType)
4813{
4814 HRESULT rc = S_OK;
4815
4816 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4817 aControllerName, aControllerPort, aDevice, aType));
4818
4819 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4820
4821 return rc;
4822}
4823
4824
4825
4826STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4827 LONG aControllerPort,
4828 LONG aDevice,
4829 BOOL aForce)
4830{
4831 int rc = S_OK;
4832 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4833 aControllerName, aControllerPort, aForce));
4834
4835 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4836
4837 return rc;
4838}
4839
4840STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4841 LONG aControllerPort,
4842 LONG aDevice,
4843 IMedium *aMedium,
4844 BOOL aForce)
4845{
4846 int rc = S_OK;
4847 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4848 aControllerName, aControllerPort, aDevice, aForce));
4849
4850 CheckComArgStrNotEmptyOrNull(aControllerName);
4851
4852 AutoCaller autoCaller(this);
4853 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4854
4855 // request the host lock first, since might be calling Host methods for getting host drives;
4856 // next, protect the media tree all the while we're in here, as well as our member variables
4857 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4858 this->lockHandle(),
4859 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4860
4861 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4862 aControllerName,
4863 aControllerPort,
4864 aDevice);
4865 if (pAttach.isNull())
4866 return setError(VBOX_E_OBJECT_NOT_FOUND,
4867 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4868 aDevice, aControllerPort, aControllerName);
4869
4870 /* Remember previously mounted medium. The medium before taking the
4871 * backup is not necessarily the same thing. */
4872 ComObjPtr<Medium> oldmedium;
4873 oldmedium = pAttach->getMedium();
4874
4875 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4876 if (aMedium && pMedium.isNull())
4877 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4878
4879 AutoCaller mediumCaller(pMedium);
4880 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4881
4882 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4883 if (pMedium)
4884 {
4885 DeviceType_T mediumType = pAttach->getType();
4886 switch (mediumType)
4887 {
4888 case DeviceType_DVD:
4889 case DeviceType_Floppy:
4890 break;
4891
4892 default:
4893 return setError(VBOX_E_INVALID_OBJECT_STATE,
4894 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4895 aControllerPort,
4896 aDevice,
4897 aControllerName);
4898 }
4899 }
4900
4901 setModified(IsModified_Storage);
4902 mMediaData.backup();
4903
4904 {
4905 // The backup operation makes the pAttach reference point to the
4906 // old settings. Re-get the correct reference.
4907 pAttach = findAttachment(mMediaData->mAttachments,
4908 aControllerName,
4909 aControllerPort,
4910 aDevice);
4911 if (!oldmedium.isNull())
4912 oldmedium->removeBackReference(mData->mUuid);
4913 if (!pMedium.isNull())
4914 {
4915 pMedium->addBackReference(mData->mUuid);
4916
4917 mediumLock.release();
4918 multiLock.release();
4919 addMediumToRegistry(pMedium);
4920 multiLock.acquire();
4921 mediumLock.acquire();
4922 }
4923
4924 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4925 pAttach->updateMedium(pMedium);
4926 }
4927
4928 setModified(IsModified_Storage);
4929
4930 mediumLock.release();
4931 multiLock.release();
4932 rc = onMediumChange(pAttach, aForce);
4933 multiLock.acquire();
4934 mediumLock.acquire();
4935
4936 /* On error roll back this change only. */
4937 if (FAILED(rc))
4938 {
4939 if (!pMedium.isNull())
4940 pMedium->removeBackReference(mData->mUuid);
4941 pAttach = findAttachment(mMediaData->mAttachments,
4942 aControllerName,
4943 aControllerPort,
4944 aDevice);
4945 /* If the attachment is gone in the meantime, bail out. */
4946 if (pAttach.isNull())
4947 return rc;
4948 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4949 if (!oldmedium.isNull())
4950 oldmedium->addBackReference(mData->mUuid);
4951 pAttach->updateMedium(oldmedium);
4952 }
4953
4954 mediumLock.release();
4955 multiLock.release();
4956
4957 mParent->saveModifiedRegistries();
4958
4959 return rc;
4960}
4961
4962STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4963 LONG aControllerPort,
4964 LONG aDevice,
4965 IMedium **aMedium)
4966{
4967 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4968 aControllerName, aControllerPort, aDevice));
4969
4970 CheckComArgStrNotEmptyOrNull(aControllerName);
4971 CheckComArgOutPointerValid(aMedium);
4972
4973 AutoCaller autoCaller(this);
4974 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4975
4976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4977
4978 *aMedium = NULL;
4979
4980 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4981 aControllerName,
4982 aControllerPort,
4983 aDevice);
4984 if (pAttach.isNull())
4985 return setError(VBOX_E_OBJECT_NOT_FOUND,
4986 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4987 aDevice, aControllerPort, aControllerName);
4988
4989 pAttach->getMedium().queryInterfaceTo(aMedium);
4990
4991 return S_OK;
4992}
4993
4994STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4995{
4996 CheckComArgOutPointerValid(port);
4997 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4998
4999 AutoCaller autoCaller(this);
5000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5001
5002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5003
5004 mSerialPorts[slot].queryInterfaceTo(port);
5005
5006 return S_OK;
5007}
5008
5009STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5010{
5011 CheckComArgOutPointerValid(port);
5012 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5013
5014 AutoCaller autoCaller(this);
5015 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5016
5017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5018
5019 mParallelPorts[slot].queryInterfaceTo(port);
5020
5021 return S_OK;
5022}
5023
5024STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5025{
5026 CheckComArgOutPointerValid(adapter);
5027 /* Do not assert if slot is out of range, just return the advertised
5028 status. testdriver/vbox.py triggers this in logVmInfo. */
5029 if (slot >= mNetworkAdapters.size())
5030 return setError(E_INVALIDARG,
5031 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5032 slot, mNetworkAdapters.size());
5033
5034 AutoCaller autoCaller(this);
5035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5036
5037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5038
5039 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5040
5041 return S_OK;
5042}
5043
5044STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5045{
5046 CheckComArgOutSafeArrayPointerValid(aKeys);
5047
5048 AutoCaller autoCaller(this);
5049 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5050
5051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5052
5053 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5054 int i = 0;
5055 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5056 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5057 ++it, ++i)
5058 {
5059 const Utf8Str &strKey = it->first;
5060 strKey.cloneTo(&saKeys[i]);
5061 }
5062 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5063
5064 return S_OK;
5065 }
5066
5067 /**
5068 * @note Locks this object for reading.
5069 */
5070STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5071 BSTR *aValue)
5072{
5073 CheckComArgStrNotEmptyOrNull(aKey);
5074 CheckComArgOutPointerValid(aValue);
5075
5076 AutoCaller autoCaller(this);
5077 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5078
5079 /* start with nothing found */
5080 Bstr bstrResult("");
5081
5082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5083
5084 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5085 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5086 // found:
5087 bstrResult = it->second; // source is a Utf8Str
5088
5089 /* return the result to caller (may be empty) */
5090 bstrResult.cloneTo(aValue);
5091
5092 return S_OK;
5093}
5094
5095 /**
5096 * @note Locks mParent for writing + this object for writing.
5097 */
5098STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5099{
5100 CheckComArgStrNotEmptyOrNull(aKey);
5101
5102 AutoCaller autoCaller(this);
5103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5104
5105 Utf8Str strKey(aKey);
5106 Utf8Str strValue(aValue);
5107 Utf8Str strOldValue; // empty
5108
5109 // locking note: we only hold the read lock briefly to look up the old value,
5110 // then release it and call the onExtraCanChange callbacks. There is a small
5111 // chance of a race insofar as the callback might be called twice if two callers
5112 // change the same key at the same time, but that's a much better solution
5113 // than the deadlock we had here before. The actual changing of the extradata
5114 // is then performed under the write lock and race-free.
5115
5116 // look up the old value first; if nothing has changed then we need not do anything
5117 {
5118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5119 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5120 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5121 strOldValue = it->second;
5122 }
5123
5124 bool fChanged;
5125 if ((fChanged = (strOldValue != strValue)))
5126 {
5127 // ask for permission from all listeners outside the locks;
5128 // onExtraDataCanChange() only briefly requests the VirtualBox
5129 // lock to copy the list of callbacks to invoke
5130 Bstr error;
5131 Bstr bstrValue(aValue);
5132
5133 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5134 {
5135 const char *sep = error.isEmpty() ? "" : ": ";
5136 CBSTR err = error.raw();
5137 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5138 sep, err));
5139 return setError(E_ACCESSDENIED,
5140 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5141 aKey,
5142 bstrValue.raw(),
5143 sep,
5144 err);
5145 }
5146
5147 // data is changing and change not vetoed: then write it out under the lock
5148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5149
5150 if (isSnapshotMachine())
5151 {
5152 HRESULT rc = checkStateDependency(MutableStateDep);
5153 if (FAILED(rc)) return rc;
5154 }
5155
5156 if (strValue.isEmpty())
5157 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5158 else
5159 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5160 // creates a new key if needed
5161
5162 bool fNeedsGlobalSaveSettings = false;
5163 saveSettings(&fNeedsGlobalSaveSettings);
5164
5165 if (fNeedsGlobalSaveSettings)
5166 {
5167 // save the global settings; for that we should hold only the VirtualBox lock
5168 alock.release();
5169 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5170 mParent->saveSettings();
5171 }
5172 }
5173
5174 // fire notification outside the lock
5175 if (fChanged)
5176 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5177
5178 return S_OK;
5179}
5180
5181STDMETHODIMP Machine::SaveSettings()
5182{
5183 AutoCaller autoCaller(this);
5184 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5185
5186 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5187
5188 /* when there was auto-conversion, we want to save the file even if
5189 * the VM is saved */
5190 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5191 if (FAILED(rc)) return rc;
5192
5193 /* the settings file path may never be null */
5194 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5195
5196 /* save all VM data excluding snapshots */
5197 bool fNeedsGlobalSaveSettings = false;
5198 rc = saveSettings(&fNeedsGlobalSaveSettings);
5199 mlock.release();
5200
5201 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5202 {
5203 // save the global settings; for that we should hold only the VirtualBox lock
5204 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5205 rc = mParent->saveSettings();
5206 }
5207
5208 return rc;
5209}
5210
5211STDMETHODIMP Machine::DiscardSettings()
5212{
5213 AutoCaller autoCaller(this);
5214 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5215
5216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5217
5218 HRESULT rc = checkStateDependency(MutableStateDep);
5219 if (FAILED(rc)) return rc;
5220
5221 /*
5222 * during this rollback, the session will be notified if data has
5223 * been actually changed
5224 */
5225 rollback(true /* aNotify */);
5226
5227 return S_OK;
5228}
5229
5230/** @note Locks objects! */
5231STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5232 ComSafeArrayOut(IMedium*, aMedia))
5233{
5234 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5235 AutoLimitedCaller autoCaller(this);
5236 AssertComRCReturnRC(autoCaller.rc());
5237
5238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5239
5240 Guid id(getId());
5241
5242 if (mData->mSession.mState != SessionState_Unlocked)
5243 return setError(VBOX_E_INVALID_OBJECT_STATE,
5244 tr("Cannot unregister the machine '%s' while it is locked"),
5245 mUserData->s.strName.c_str());
5246
5247 // wait for state dependents to drop to zero
5248 ensureNoStateDependencies();
5249
5250 if (!mData->mAccessible)
5251 {
5252 // inaccessible maschines can only be unregistered; uninitialize ourselves
5253 // here because currently there may be no unregistered that are inaccessible
5254 // (this state combination is not supported). Note releasing the caller and
5255 // leaving the lock before calling uninit()
5256 alock.release();
5257 autoCaller.release();
5258
5259 uninit();
5260
5261 mParent->unregisterMachine(this, id);
5262 // calls VirtualBox::saveSettings()
5263
5264 return S_OK;
5265 }
5266
5267 HRESULT rc = S_OK;
5268
5269 // discard saved state
5270 if (mData->mMachineState == MachineState_Saved)
5271 {
5272 // add the saved state file to the list of files the caller should delete
5273 Assert(!mSSData->strStateFilePath.isEmpty());
5274 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5275
5276 mSSData->strStateFilePath.setNull();
5277
5278 // unconditionally set the machine state to powered off, we now
5279 // know no session has locked the machine
5280 mData->mMachineState = MachineState_PoweredOff;
5281 }
5282
5283 size_t cSnapshots = 0;
5284 if (mData->mFirstSnapshot)
5285 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5286 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5287 // fail now before we start detaching media
5288 return setError(VBOX_E_INVALID_OBJECT_STATE,
5289 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5290 mUserData->s.strName.c_str(), cSnapshots);
5291
5292 // This list collects the medium objects from all medium attachments
5293 // which we will detach from the machine and its snapshots, in a specific
5294 // order which allows for closing all media without getting "media in use"
5295 // errors, simply by going through the list from the front to the back:
5296 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5297 // and must be closed before the parent media from the snapshots, or closing the parents
5298 // will fail because they still have children);
5299 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5300 // the root ("first") snapshot of the machine.
5301 MediaList llMedia;
5302
5303 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5304 && mMediaData->mAttachments.size()
5305 )
5306 {
5307 // we have media attachments: detach them all and add the Medium objects to our list
5308 if (cleanupMode != CleanupMode_UnregisterOnly)
5309 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5310 else
5311 return setError(VBOX_E_INVALID_OBJECT_STATE,
5312 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5313 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5314 }
5315
5316 if (cSnapshots)
5317 {
5318 // autoCleanup must be true here, or we would have failed above
5319
5320 // add the media from the medium attachments of the snapshots to llMedia
5321 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5322 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5323 // into the children first
5324
5325 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5326 MachineState_T oldState = mData->mMachineState;
5327 mData->mMachineState = MachineState_DeletingSnapshot;
5328
5329 // make a copy of the first snapshot so the refcount does not drop to 0
5330 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5331 // because of the AutoCaller voodoo)
5332 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5333
5334 // GO!
5335 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5336
5337 mData->mMachineState = oldState;
5338 }
5339
5340 if (FAILED(rc))
5341 {
5342 rollbackMedia();
5343 return rc;
5344 }
5345
5346 // commit all the media changes made above
5347 commitMedia();
5348
5349 mData->mRegistered = false;
5350
5351 // machine lock no longer needed
5352 alock.release();
5353
5354 // return media to caller
5355 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5356 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5357
5358 mParent->unregisterMachine(this, id);
5359 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5360
5361 return S_OK;
5362}
5363
5364struct Machine::DeleteTask
5365{
5366 ComObjPtr<Machine> pMachine;
5367 RTCList<ComPtr<IMedium> > llMediums;
5368 StringsList llFilesToDelete;
5369 ComObjPtr<Progress> pProgress;
5370};
5371
5372STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5373{
5374 LogFlowFuncEnter();
5375
5376 AutoCaller autoCaller(this);
5377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5378
5379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5380
5381 HRESULT rc = checkStateDependency(MutableStateDep);
5382 if (FAILED(rc)) return rc;
5383
5384 if (mData->mRegistered)
5385 return setError(VBOX_E_INVALID_VM_STATE,
5386 tr("Cannot delete settings of a registered machine"));
5387
5388 DeleteTask *pTask = new DeleteTask;
5389 pTask->pMachine = this;
5390 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5391
5392 // collect files to delete
5393 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5394
5395 for (size_t i = 0; i < sfaMedia.size(); ++i)
5396 {
5397 IMedium *pIMedium(sfaMedia[i]);
5398 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5399 if (pMedium.isNull())
5400 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5401 SafeArray<BSTR> ids;
5402 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5403 if (FAILED(rc)) return rc;
5404 /* At this point the medium should not have any back references
5405 * anymore. If it has it is attached to another VM and *must* not
5406 * deleted. */
5407 if (ids.size() < 1)
5408 pTask->llMediums.append(pMedium);
5409 }
5410 if (mData->pMachineConfigFile->fileExists())
5411 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5412
5413 pTask->pProgress.createObject();
5414 pTask->pProgress->init(getVirtualBox(),
5415 static_cast<IMachine*>(this) /* aInitiator */,
5416 Bstr(tr("Deleting files")).raw(),
5417 true /* fCancellable */,
5418 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5419 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5420
5421 int vrc = RTThreadCreate(NULL,
5422 Machine::deleteThread,
5423 (void*)pTask,
5424 0,
5425 RTTHREADTYPE_MAIN_WORKER,
5426 0,
5427 "MachineDelete");
5428
5429 pTask->pProgress.queryInterfaceTo(aProgress);
5430
5431 if (RT_FAILURE(vrc))
5432 {
5433 delete pTask;
5434 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5435 }
5436
5437 LogFlowFuncLeave();
5438
5439 return S_OK;
5440}
5441
5442/**
5443 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5444 * calls Machine::deleteTaskWorker() on the actual machine object.
5445 * @param Thread
5446 * @param pvUser
5447 * @return
5448 */
5449/*static*/
5450DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5451{
5452 LogFlowFuncEnter();
5453
5454 DeleteTask *pTask = (DeleteTask*)pvUser;
5455 Assert(pTask);
5456 Assert(pTask->pMachine);
5457 Assert(pTask->pProgress);
5458
5459 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5460 pTask->pProgress->notifyComplete(rc);
5461
5462 delete pTask;
5463
5464 LogFlowFuncLeave();
5465
5466 NOREF(Thread);
5467
5468 return VINF_SUCCESS;
5469}
5470
5471/**
5472 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5473 * @param task
5474 * @return
5475 */
5476HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5477{
5478 AutoCaller autoCaller(this);
5479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5480
5481 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5482
5483 HRESULT rc = S_OK;
5484
5485 try
5486 {
5487 ULONG uLogHistoryCount = 3;
5488 ComPtr<ISystemProperties> systemProperties;
5489 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5490 if (FAILED(rc)) throw rc;
5491
5492 if (!systemProperties.isNull())
5493 {
5494 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5495 if (FAILED(rc)) throw rc;
5496 }
5497
5498 MachineState_T oldState = mData->mMachineState;
5499 setMachineState(MachineState_SettingUp);
5500 alock.release();
5501 for (size_t i = 0; i < task.llMediums.size(); ++i)
5502 {
5503 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5504 {
5505 AutoCaller mac(pMedium);
5506 if (FAILED(mac.rc())) throw mac.rc();
5507 Utf8Str strLocation = pMedium->getLocationFull();
5508 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5509 if (FAILED(rc)) throw rc;
5510 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5511 }
5512 ComPtr<IProgress> pProgress2;
5513 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5514 if (FAILED(rc)) throw rc;
5515 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5516 if (FAILED(rc)) throw rc;
5517 /* Check the result of the asynchrony process. */
5518 LONG iRc;
5519 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5520 if (FAILED(rc)) throw rc;
5521 /* If the thread of the progress object has an error, then
5522 * retrieve the error info from there, or it'll be lost. */
5523 if (FAILED(iRc))
5524 throw setError(ProgressErrorInfo(pProgress2));
5525 }
5526 setMachineState(oldState);
5527 alock.acquire();
5528
5529 // delete the files pushed on the task list by Machine::Delete()
5530 // (this includes saved states of the machine and snapshots and
5531 // medium storage files from the IMedium list passed in, and the
5532 // machine XML file)
5533 StringsList::const_iterator it = task.llFilesToDelete.begin();
5534 while (it != task.llFilesToDelete.end())
5535 {
5536 const Utf8Str &strFile = *it;
5537 LogFunc(("Deleting file %s\n", strFile.c_str()));
5538 int vrc = RTFileDelete(strFile.c_str());
5539 if (RT_FAILURE(vrc))
5540 throw setError(VBOX_E_IPRT_ERROR,
5541 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5542
5543 ++it;
5544 if (it == task.llFilesToDelete.end())
5545 {
5546 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5547 if (FAILED(rc)) throw rc;
5548 break;
5549 }
5550
5551 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5552 if (FAILED(rc)) throw rc;
5553 }
5554
5555 /* delete the settings only when the file actually exists */
5556 if (mData->pMachineConfigFile->fileExists())
5557 {
5558 /* Delete any backup or uncommitted XML files. Ignore failures.
5559 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5560 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5561 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5562 RTFileDelete(otherXml.c_str());
5563 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5564 RTFileDelete(otherXml.c_str());
5565
5566 /* delete the Logs folder, nothing important should be left
5567 * there (we don't check for errors because the user might have
5568 * some private files there that we don't want to delete) */
5569 Utf8Str logFolder;
5570 getLogFolder(logFolder);
5571 Assert(logFolder.length());
5572 if (RTDirExists(logFolder.c_str()))
5573 {
5574 /* Delete all VBox.log[.N] files from the Logs folder
5575 * (this must be in sync with the rotation logic in
5576 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5577 * files that may have been created by the GUI. */
5578 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5579 logFolder.c_str(), RTPATH_DELIMITER);
5580 RTFileDelete(log.c_str());
5581 log = Utf8StrFmt("%s%cVBox.png",
5582 logFolder.c_str(), RTPATH_DELIMITER);
5583 RTFileDelete(log.c_str());
5584 for (int i = uLogHistoryCount; i > 0; i--)
5585 {
5586 log = Utf8StrFmt("%s%cVBox.log.%d",
5587 logFolder.c_str(), RTPATH_DELIMITER, i);
5588 RTFileDelete(log.c_str());
5589 log = Utf8StrFmt("%s%cVBox.png.%d",
5590 logFolder.c_str(), RTPATH_DELIMITER, i);
5591 RTFileDelete(log.c_str());
5592 }
5593
5594 RTDirRemove(logFolder.c_str());
5595 }
5596
5597 /* delete the Snapshots folder, nothing important should be left
5598 * there (we don't check for errors because the user might have
5599 * some private files there that we don't want to delete) */
5600 Utf8Str strFullSnapshotFolder;
5601 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5602 Assert(!strFullSnapshotFolder.isEmpty());
5603 if (RTDirExists(strFullSnapshotFolder.c_str()))
5604 RTDirRemove(strFullSnapshotFolder.c_str());
5605
5606 // delete the directory that contains the settings file, but only
5607 // if it matches the VM name
5608 Utf8Str settingsDir;
5609 if (isInOwnDir(&settingsDir))
5610 RTDirRemove(settingsDir.c_str());
5611 }
5612
5613 alock.release();
5614
5615 mParent->saveModifiedRegistries();
5616 }
5617 catch (HRESULT aRC) { rc = aRC; }
5618
5619 return rc;
5620}
5621
5622STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5623{
5624 CheckComArgOutPointerValid(aSnapshot);
5625
5626 AutoCaller autoCaller(this);
5627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5628
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630
5631 ComObjPtr<Snapshot> pSnapshot;
5632 HRESULT rc;
5633
5634 if (!aNameOrId || !*aNameOrId)
5635 // null case (caller wants root snapshot): findSnapshotById() handles this
5636 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5637 else
5638 {
5639 Guid uuid(aNameOrId);
5640 if (uuid.isValid())
5641 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5642 else
5643 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5644 }
5645 pSnapshot.queryInterfaceTo(aSnapshot);
5646
5647 return rc;
5648}
5649
5650STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5651{
5652 CheckComArgStrNotEmptyOrNull(aName);
5653 CheckComArgStrNotEmptyOrNull(aHostPath);
5654
5655 AutoCaller autoCaller(this);
5656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5657
5658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5659
5660 HRESULT rc = checkStateDependency(MutableStateDep);
5661 if (FAILED(rc)) return rc;
5662
5663 Utf8Str strName(aName);
5664
5665 ComObjPtr<SharedFolder> sharedFolder;
5666 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5667 if (SUCCEEDED(rc))
5668 return setError(VBOX_E_OBJECT_IN_USE,
5669 tr("Shared folder named '%s' already exists"),
5670 strName.c_str());
5671
5672 sharedFolder.createObject();
5673 rc = sharedFolder->init(getMachine(),
5674 strName,
5675 aHostPath,
5676 !!aWritable,
5677 !!aAutoMount,
5678 true /* fFailOnError */);
5679 if (FAILED(rc)) return rc;
5680
5681 setModified(IsModified_SharedFolders);
5682 mHWData.backup();
5683 mHWData->mSharedFolders.push_back(sharedFolder);
5684
5685 /* inform the direct session if any */
5686 alock.release();
5687 onSharedFolderChange();
5688
5689 return S_OK;
5690}
5691
5692STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5693{
5694 CheckComArgStrNotEmptyOrNull(aName);
5695
5696 AutoCaller autoCaller(this);
5697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5698
5699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5700
5701 HRESULT rc = checkStateDependency(MutableStateDep);
5702 if (FAILED(rc)) return rc;
5703
5704 ComObjPtr<SharedFolder> sharedFolder;
5705 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5706 if (FAILED(rc)) return rc;
5707
5708 setModified(IsModified_SharedFolders);
5709 mHWData.backup();
5710 mHWData->mSharedFolders.remove(sharedFolder);
5711
5712 /* inform the direct session if any */
5713 alock.release();
5714 onSharedFolderChange();
5715
5716 return S_OK;
5717}
5718
5719STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5720{
5721 CheckComArgOutPointerValid(aCanShow);
5722
5723 /* start with No */
5724 *aCanShow = FALSE;
5725
5726 AutoCaller autoCaller(this);
5727 AssertComRCReturnRC(autoCaller.rc());
5728
5729 ComPtr<IInternalSessionControl> directControl;
5730 {
5731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5732
5733 if (mData->mSession.mState != SessionState_Locked)
5734 return setError(VBOX_E_INVALID_VM_STATE,
5735 tr("Machine is not locked for session (session state: %s)"),
5736 Global::stringifySessionState(mData->mSession.mState));
5737
5738 directControl = mData->mSession.mDirectControl;
5739 }
5740
5741 /* ignore calls made after #OnSessionEnd() is called */
5742 if (!directControl)
5743 return S_OK;
5744
5745 LONG64 dummy;
5746 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5747}
5748
5749STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5750{
5751 CheckComArgOutPointerValid(aWinId);
5752
5753 AutoCaller autoCaller(this);
5754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5755
5756 ComPtr<IInternalSessionControl> directControl;
5757 {
5758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5759
5760 if (mData->mSession.mState != SessionState_Locked)
5761 return setError(E_FAIL,
5762 tr("Machine is not locked for session (session state: %s)"),
5763 Global::stringifySessionState(mData->mSession.mState));
5764
5765 directControl = mData->mSession.mDirectControl;
5766 }
5767
5768 /* ignore calls made after #OnSessionEnd() is called */
5769 if (!directControl)
5770 return S_OK;
5771
5772 BOOL dummy;
5773 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5774}
5775
5776#ifdef VBOX_WITH_GUEST_PROPS
5777/**
5778 * Look up a guest property in VBoxSVC's internal structures.
5779 */
5780HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5781 BSTR *aValue,
5782 LONG64 *aTimestamp,
5783 BSTR *aFlags) const
5784{
5785 using namespace guestProp;
5786
5787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5788 Utf8Str strName(aName);
5789 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5790
5791 if (it != mHWData->mGuestProperties.end())
5792 {
5793 char szFlags[MAX_FLAGS_LEN + 1];
5794 it->second.strValue.cloneTo(aValue);
5795 *aTimestamp = it->second.mTimestamp;
5796 writeFlags(it->second.mFlags, szFlags);
5797 Bstr(szFlags).cloneTo(aFlags);
5798 }
5799
5800 return S_OK;
5801}
5802
5803/**
5804 * Query the VM that a guest property belongs to for the property.
5805 * @returns E_ACCESSDENIED if the VM process is not available or not
5806 * currently handling queries and the lookup should then be done in
5807 * VBoxSVC.
5808 */
5809HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5810 BSTR *aValue,
5811 LONG64 *aTimestamp,
5812 BSTR *aFlags) const
5813{
5814 HRESULT rc;
5815 ComPtr<IInternalSessionControl> directControl;
5816 directControl = mData->mSession.mDirectControl;
5817
5818 /* fail if we were called after #OnSessionEnd() is called. This is a
5819 * silly race condition. */
5820
5821 if (!directControl)
5822 rc = E_ACCESSDENIED;
5823 else
5824 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5825 false /* isSetter */,
5826 aValue, aTimestamp, aFlags);
5827 return rc;
5828}
5829#endif // VBOX_WITH_GUEST_PROPS
5830
5831STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5832 BSTR *aValue,
5833 LONG64 *aTimestamp,
5834 BSTR *aFlags)
5835{
5836#ifndef VBOX_WITH_GUEST_PROPS
5837 ReturnComNotImplemented();
5838#else // VBOX_WITH_GUEST_PROPS
5839 CheckComArgStrNotEmptyOrNull(aName);
5840 CheckComArgOutPointerValid(aValue);
5841 CheckComArgOutPointerValid(aTimestamp);
5842 CheckComArgOutPointerValid(aFlags);
5843
5844 AutoCaller autoCaller(this);
5845 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5846
5847 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5848 if (rc == E_ACCESSDENIED)
5849 /* The VM is not running or the service is not (yet) accessible */
5850 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5851 return rc;
5852#endif // VBOX_WITH_GUEST_PROPS
5853}
5854
5855STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5856{
5857 LONG64 dummyTimestamp;
5858 Bstr dummyFlags;
5859 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5860}
5861
5862STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5863{
5864 Bstr dummyValue;
5865 Bstr dummyFlags;
5866 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5867}
5868
5869#ifdef VBOX_WITH_GUEST_PROPS
5870/**
5871 * Set a guest property in VBoxSVC's internal structures.
5872 */
5873HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5874 IN_BSTR aFlags)
5875{
5876 using namespace guestProp;
5877
5878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5879 HRESULT rc = S_OK;
5880
5881 rc = checkStateDependency(MutableStateDep);
5882 if (FAILED(rc)) return rc;
5883
5884 try
5885 {
5886 Utf8Str utf8Name(aName);
5887 Utf8Str utf8Flags(aFlags);
5888 uint32_t fFlags = NILFLAG;
5889 if ( aFlags != NULL
5890 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5891 return setError(E_INVALIDARG,
5892 tr("Invalid guest property flag values: '%ls'"),
5893 aFlags);
5894
5895 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5896 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5897 if (it == mHWData->mGuestProperties.end())
5898 {
5899 if (!fDelete)
5900 {
5901 setModified(IsModified_MachineData);
5902 mHWData.backupEx();
5903
5904 RTTIMESPEC time;
5905 HWData::GuestProperty prop;
5906 prop.strValue = aValue;
5907 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5908 prop.mFlags = fFlags;
5909 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5910 }
5911 }
5912 else
5913 {
5914 if (it->second.mFlags & (RDONLYHOST))
5915 {
5916 rc = setError(E_ACCESSDENIED,
5917 tr("The property '%ls' cannot be changed by the host"),
5918 aName);
5919 }
5920 else
5921 {
5922 setModified(IsModified_MachineData);
5923 mHWData.backupEx();
5924
5925 /* The backupEx() operation invalidates our iterator,
5926 * so get a new one. */
5927 it = mHWData->mGuestProperties.find(utf8Name);
5928 Assert(it != mHWData->mGuestProperties.end());
5929
5930 if (!fDelete)
5931 {
5932 RTTIMESPEC time;
5933 it->second.strValue = aValue;
5934 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5935 it->second.mFlags = fFlags;
5936 }
5937 else
5938 mHWData->mGuestProperties.erase(it);
5939 }
5940 }
5941
5942 if ( SUCCEEDED(rc)
5943 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5944 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5945 RTSTR_MAX,
5946 utf8Name.c_str(),
5947 RTSTR_MAX,
5948 NULL)
5949 )
5950 )
5951 {
5952 alock.release();
5953
5954 mParent->onGuestPropertyChange(mData->mUuid, aName,
5955 aValue ? aValue : Bstr("").raw(),
5956 aFlags ? aFlags : Bstr("").raw());
5957 }
5958 }
5959 catch (std::bad_alloc &)
5960 {
5961 rc = E_OUTOFMEMORY;
5962 }
5963
5964 return rc;
5965}
5966
5967/**
5968 * Set a property on the VM that that property belongs to.
5969 * @returns E_ACCESSDENIED if the VM process is not available or not
5970 * currently handling queries and the setting should then be done in
5971 * VBoxSVC.
5972 */
5973HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5974 IN_BSTR aFlags)
5975{
5976 HRESULT rc;
5977
5978 try
5979 {
5980 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5981
5982 BSTR dummy = NULL; /* will not be changed (setter) */
5983 LONG64 dummy64;
5984 if (!directControl)
5985 rc = E_ACCESSDENIED;
5986 else
5987 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5988 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5989 true /* isSetter */,
5990 &dummy, &dummy64, &dummy);
5991 }
5992 catch (std::bad_alloc &)
5993 {
5994 rc = E_OUTOFMEMORY;
5995 }
5996
5997 return rc;
5998}
5999#endif // VBOX_WITH_GUEST_PROPS
6000
6001STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6002 IN_BSTR aFlags)
6003{
6004#ifndef VBOX_WITH_GUEST_PROPS
6005 ReturnComNotImplemented();
6006#else // VBOX_WITH_GUEST_PROPS
6007 CheckComArgStrNotEmptyOrNull(aName);
6008 CheckComArgMaybeNull(aFlags);
6009 CheckComArgMaybeNull(aValue);
6010
6011 AutoCaller autoCaller(this);
6012 if (FAILED(autoCaller.rc()))
6013 return autoCaller.rc();
6014
6015 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6016 if (rc == E_ACCESSDENIED)
6017 /* The VM is not running or the service is not (yet) accessible */
6018 rc = setGuestPropertyToService(aName, aValue, aFlags);
6019 return rc;
6020#endif // VBOX_WITH_GUEST_PROPS
6021}
6022
6023STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6024{
6025 return SetGuestProperty(aName, aValue, NULL);
6026}
6027
6028STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6029{
6030 return SetGuestProperty(aName, NULL, NULL);
6031}
6032
6033#ifdef VBOX_WITH_GUEST_PROPS
6034/**
6035 * Enumerate the guest properties in VBoxSVC's internal structures.
6036 */
6037HRESULT Machine::enumerateGuestPropertiesInService
6038 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6039 ComSafeArrayOut(BSTR, aValues),
6040 ComSafeArrayOut(LONG64, aTimestamps),
6041 ComSafeArrayOut(BSTR, aFlags))
6042{
6043 using namespace guestProp;
6044
6045 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6046 Utf8Str strPatterns(aPatterns);
6047
6048 HWData::GuestPropertyMap propMap;
6049
6050 /*
6051 * Look for matching patterns and build up a list.
6052 */
6053 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6054 while (it != mHWData->mGuestProperties.end())
6055 {
6056 if ( strPatterns.isEmpty()
6057 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6058 RTSTR_MAX,
6059 it->first.c_str(),
6060 RTSTR_MAX,
6061 NULL)
6062 )
6063 {
6064 propMap.insert(*it);
6065 }
6066
6067 it++;
6068 }
6069
6070 alock.release();
6071
6072 /*
6073 * And build up the arrays for returning the property information.
6074 */
6075 size_t cEntries = propMap.size();
6076 SafeArray<BSTR> names(cEntries);
6077 SafeArray<BSTR> values(cEntries);
6078 SafeArray<LONG64> timestamps(cEntries);
6079 SafeArray<BSTR> flags(cEntries);
6080 size_t iProp = 0;
6081
6082 it = propMap.begin();
6083 while (it != propMap.end())
6084 {
6085 char szFlags[MAX_FLAGS_LEN + 1];
6086 it->first.cloneTo(&names[iProp]);
6087 it->second.strValue.cloneTo(&values[iProp]);
6088 timestamps[iProp] = it->second.mTimestamp;
6089 writeFlags(it->second.mFlags, szFlags);
6090 Bstr(szFlags).cloneTo(&flags[iProp++]);
6091 it++;
6092 }
6093 names.detachTo(ComSafeArrayOutArg(aNames));
6094 values.detachTo(ComSafeArrayOutArg(aValues));
6095 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6096 flags.detachTo(ComSafeArrayOutArg(aFlags));
6097 return S_OK;
6098}
6099
6100/**
6101 * Enumerate the properties managed by a VM.
6102 * @returns E_ACCESSDENIED if the VM process is not available or not
6103 * currently handling queries and the setting should then be done in
6104 * VBoxSVC.
6105 */
6106HRESULT Machine::enumerateGuestPropertiesOnVM
6107 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6108 ComSafeArrayOut(BSTR, aValues),
6109 ComSafeArrayOut(LONG64, aTimestamps),
6110 ComSafeArrayOut(BSTR, aFlags))
6111{
6112 HRESULT rc;
6113 ComPtr<IInternalSessionControl> directControl;
6114 directControl = mData->mSession.mDirectControl;
6115
6116 if (!directControl)
6117 rc = E_ACCESSDENIED;
6118 else
6119 rc = directControl->EnumerateGuestProperties
6120 (aPatterns, ComSafeArrayOutArg(aNames),
6121 ComSafeArrayOutArg(aValues),
6122 ComSafeArrayOutArg(aTimestamps),
6123 ComSafeArrayOutArg(aFlags));
6124 return rc;
6125}
6126#endif // VBOX_WITH_GUEST_PROPS
6127
6128STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6129 ComSafeArrayOut(BSTR, aNames),
6130 ComSafeArrayOut(BSTR, aValues),
6131 ComSafeArrayOut(LONG64, aTimestamps),
6132 ComSafeArrayOut(BSTR, aFlags))
6133{
6134#ifndef VBOX_WITH_GUEST_PROPS
6135 ReturnComNotImplemented();
6136#else // VBOX_WITH_GUEST_PROPS
6137 CheckComArgMaybeNull(aPatterns);
6138 CheckComArgOutSafeArrayPointerValid(aNames);
6139 CheckComArgOutSafeArrayPointerValid(aValues);
6140 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6141 CheckComArgOutSafeArrayPointerValid(aFlags);
6142
6143 AutoCaller autoCaller(this);
6144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6145
6146 HRESULT rc = enumerateGuestPropertiesOnVM
6147 (aPatterns, ComSafeArrayOutArg(aNames),
6148 ComSafeArrayOutArg(aValues),
6149 ComSafeArrayOutArg(aTimestamps),
6150 ComSafeArrayOutArg(aFlags));
6151 if (rc == E_ACCESSDENIED)
6152 /* The VM is not running or the service is not (yet) accessible */
6153 rc = enumerateGuestPropertiesInService
6154 (aPatterns, ComSafeArrayOutArg(aNames),
6155 ComSafeArrayOutArg(aValues),
6156 ComSafeArrayOutArg(aTimestamps),
6157 ComSafeArrayOutArg(aFlags));
6158 return rc;
6159#endif // VBOX_WITH_GUEST_PROPS
6160}
6161
6162STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6163 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6164{
6165 MediaData::AttachmentList atts;
6166
6167 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6168 if (FAILED(rc)) return rc;
6169
6170 SafeIfaceArray<IMediumAttachment> attachments(atts);
6171 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6172
6173 return S_OK;
6174}
6175
6176STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6177 LONG aControllerPort,
6178 LONG aDevice,
6179 IMediumAttachment **aAttachment)
6180{
6181 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6182 aControllerName, aControllerPort, aDevice));
6183
6184 CheckComArgStrNotEmptyOrNull(aControllerName);
6185 CheckComArgOutPointerValid(aAttachment);
6186
6187 AutoCaller autoCaller(this);
6188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6189
6190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6191
6192 *aAttachment = NULL;
6193
6194 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6195 aControllerName,
6196 aControllerPort,
6197 aDevice);
6198 if (pAttach.isNull())
6199 return setError(VBOX_E_OBJECT_NOT_FOUND,
6200 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6201 aDevice, aControllerPort, aControllerName);
6202
6203 pAttach.queryInterfaceTo(aAttachment);
6204
6205 return S_OK;
6206}
6207
6208STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6209 StorageBus_T aConnectionType,
6210 IStorageController **controller)
6211{
6212 CheckComArgStrNotEmptyOrNull(aName);
6213
6214 if ( (aConnectionType <= StorageBus_Null)
6215 || (aConnectionType > StorageBus_SAS))
6216 return setError(E_INVALIDARG,
6217 tr("Invalid connection type: %d"),
6218 aConnectionType);
6219
6220 AutoCaller autoCaller(this);
6221 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6222
6223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 HRESULT rc = checkStateDependency(MutableStateDep);
6226 if (FAILED(rc)) return rc;
6227
6228 /* try to find one with the name first. */
6229 ComObjPtr<StorageController> ctrl;
6230
6231 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6232 if (SUCCEEDED(rc))
6233 return setError(VBOX_E_OBJECT_IN_USE,
6234 tr("Storage controller named '%ls' already exists"),
6235 aName);
6236
6237 ctrl.createObject();
6238
6239 /* get a new instance number for the storage controller */
6240 ULONG ulInstance = 0;
6241 bool fBootable = true;
6242 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6243 it != mStorageControllers->end();
6244 ++it)
6245 {
6246 if ((*it)->getStorageBus() == aConnectionType)
6247 {
6248 ULONG ulCurInst = (*it)->getInstance();
6249
6250 if (ulCurInst >= ulInstance)
6251 ulInstance = ulCurInst + 1;
6252
6253 /* Only one controller of each type can be marked as bootable. */
6254 if ((*it)->getBootable())
6255 fBootable = false;
6256 }
6257 }
6258
6259 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6260 if (FAILED(rc)) return rc;
6261
6262 setModified(IsModified_Storage);
6263 mStorageControllers.backup();
6264 mStorageControllers->push_back(ctrl);
6265
6266 ctrl.queryInterfaceTo(controller);
6267
6268 /* inform the direct session if any */
6269 alock.release();
6270 onStorageControllerChange();
6271
6272 return S_OK;
6273}
6274
6275STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6276 IStorageController **aStorageController)
6277{
6278 CheckComArgStrNotEmptyOrNull(aName);
6279
6280 AutoCaller autoCaller(this);
6281 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6282
6283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6284
6285 ComObjPtr<StorageController> ctrl;
6286
6287 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6288 if (SUCCEEDED(rc))
6289 ctrl.queryInterfaceTo(aStorageController);
6290
6291 return rc;
6292}
6293
6294STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6295 IStorageController **aStorageController)
6296{
6297 AutoCaller autoCaller(this);
6298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6299
6300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6301
6302 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6303 it != mStorageControllers->end();
6304 ++it)
6305 {
6306 if ((*it)->getInstance() == aInstance)
6307 {
6308 (*it).queryInterfaceTo(aStorageController);
6309 return S_OK;
6310 }
6311 }
6312
6313 return setError(VBOX_E_OBJECT_NOT_FOUND,
6314 tr("Could not find a storage controller with instance number '%lu'"),
6315 aInstance);
6316}
6317
6318STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6319{
6320 AutoCaller autoCaller(this);
6321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6322
6323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6324
6325 HRESULT rc = checkStateDependency(MutableStateDep);
6326 if (FAILED(rc)) return rc;
6327
6328 ComObjPtr<StorageController> ctrl;
6329
6330 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6331 if (SUCCEEDED(rc))
6332 {
6333 /* Ensure that only one controller of each type is marked as bootable. */
6334 if (fBootable == TRUE)
6335 {
6336 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6337 it != mStorageControllers->end();
6338 ++it)
6339 {
6340 ComObjPtr<StorageController> aCtrl = (*it);
6341
6342 if ( (aCtrl->getName() != Utf8Str(aName))
6343 && aCtrl->getBootable() == TRUE
6344 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6345 && aCtrl->getControllerType() == ctrl->getControllerType())
6346 {
6347 aCtrl->setBootable(FALSE);
6348 break;
6349 }
6350 }
6351 }
6352
6353 if (SUCCEEDED(rc))
6354 {
6355 ctrl->setBootable(fBootable);
6356 setModified(IsModified_Storage);
6357 }
6358 }
6359
6360 if (SUCCEEDED(rc))
6361 {
6362 /* inform the direct session if any */
6363 alock.release();
6364 onStorageControllerChange();
6365 }
6366
6367 return rc;
6368}
6369
6370STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6371{
6372 CheckComArgStrNotEmptyOrNull(aName);
6373
6374 AutoCaller autoCaller(this);
6375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6376
6377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 HRESULT rc = checkStateDependency(MutableStateDep);
6380 if (FAILED(rc)) return rc;
6381
6382 ComObjPtr<StorageController> ctrl;
6383 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6384 if (FAILED(rc)) return rc;
6385
6386 {
6387 /* find all attached devices to the appropriate storage controller and detach them all */
6388 // make a temporary list because detachDevice invalidates iterators into
6389 // mMediaData->mAttachments
6390 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6391
6392 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6393 it != llAttachments2.end();
6394 ++it)
6395 {
6396 MediumAttachment *pAttachTemp = *it;
6397
6398 AutoCaller localAutoCaller(pAttachTemp);
6399 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6400
6401 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6402
6403 if (pAttachTemp->getControllerName() == aName)
6404 {
6405 rc = detachDevice(pAttachTemp, alock, NULL);
6406 if (FAILED(rc)) return rc;
6407 }
6408 }
6409 }
6410
6411 /* We can remove it now. */
6412 setModified(IsModified_Storage);
6413 mStorageControllers.backup();
6414
6415 ctrl->unshare();
6416
6417 mStorageControllers->remove(ctrl);
6418
6419 /* inform the direct session if any */
6420 alock.release();
6421 onStorageControllerChange();
6422
6423 return S_OK;
6424}
6425
6426STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6427 ULONG *puOriginX,
6428 ULONG *puOriginY,
6429 ULONG *puWidth,
6430 ULONG *puHeight,
6431 BOOL *pfEnabled)
6432{
6433 LogFlowThisFunc(("\n"));
6434
6435 CheckComArgNotNull(puOriginX);
6436 CheckComArgNotNull(puOriginY);
6437 CheckComArgNotNull(puWidth);
6438 CheckComArgNotNull(puHeight);
6439 CheckComArgNotNull(pfEnabled);
6440
6441 uint32_t u32OriginX= 0;
6442 uint32_t u32OriginY= 0;
6443 uint32_t u32Width = 0;
6444 uint32_t u32Height = 0;
6445 uint16_t u16Flags = 0;
6446
6447 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6448 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6449 if (RT_FAILURE(vrc))
6450 {
6451#ifdef RT_OS_WINDOWS
6452 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6453 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6454 * So just assign fEnable to TRUE again.
6455 * The right fix would be to change GUI API wrappers to make sure that parameters
6456 * are changed only if API succeeds.
6457 */
6458 *pfEnabled = TRUE;
6459#endif
6460 return setError(VBOX_E_IPRT_ERROR,
6461 tr("Saved guest size is not available (%Rrc)"),
6462 vrc);
6463 }
6464
6465 *puOriginX = u32OriginX;
6466 *puOriginY = u32OriginY;
6467 *puWidth = u32Width;
6468 *puHeight = u32Height;
6469 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6470
6471 return S_OK;
6472}
6473
6474STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6475{
6476 LogFlowThisFunc(("\n"));
6477
6478 CheckComArgNotNull(aSize);
6479 CheckComArgNotNull(aWidth);
6480 CheckComArgNotNull(aHeight);
6481
6482 if (aScreenId != 0)
6483 return E_NOTIMPL;
6484
6485 AutoCaller autoCaller(this);
6486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6487
6488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6489
6490 uint8_t *pu8Data = NULL;
6491 uint32_t cbData = 0;
6492 uint32_t u32Width = 0;
6493 uint32_t u32Height = 0;
6494
6495 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6496
6497 if (RT_FAILURE(vrc))
6498 return setError(VBOX_E_IPRT_ERROR,
6499 tr("Saved screenshot data is not available (%Rrc)"),
6500 vrc);
6501
6502 *aSize = cbData;
6503 *aWidth = u32Width;
6504 *aHeight = u32Height;
6505
6506 freeSavedDisplayScreenshot(pu8Data);
6507
6508 return S_OK;
6509}
6510
6511STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6512{
6513 LogFlowThisFunc(("\n"));
6514
6515 CheckComArgNotNull(aWidth);
6516 CheckComArgNotNull(aHeight);
6517 CheckComArgOutSafeArrayPointerValid(aData);
6518
6519 if (aScreenId != 0)
6520 return E_NOTIMPL;
6521
6522 AutoCaller autoCaller(this);
6523 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6524
6525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6526
6527 uint8_t *pu8Data = NULL;
6528 uint32_t cbData = 0;
6529 uint32_t u32Width = 0;
6530 uint32_t u32Height = 0;
6531
6532 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6533
6534 if (RT_FAILURE(vrc))
6535 return setError(VBOX_E_IPRT_ERROR,
6536 tr("Saved screenshot data is not available (%Rrc)"),
6537 vrc);
6538
6539 *aWidth = u32Width;
6540 *aHeight = u32Height;
6541
6542 com::SafeArray<BYTE> bitmap(cbData);
6543 /* Convert pixels to format expected by the API caller. */
6544 if (aBGR)
6545 {
6546 /* [0] B, [1] G, [2] R, [3] A. */
6547 for (unsigned i = 0; i < cbData; i += 4)
6548 {
6549 bitmap[i] = pu8Data[i];
6550 bitmap[i + 1] = pu8Data[i + 1];
6551 bitmap[i + 2] = pu8Data[i + 2];
6552 bitmap[i + 3] = 0xff;
6553 }
6554 }
6555 else
6556 {
6557 /* [0] R, [1] G, [2] B, [3] A. */
6558 for (unsigned i = 0; i < cbData; i += 4)
6559 {
6560 bitmap[i] = pu8Data[i + 2];
6561 bitmap[i + 1] = pu8Data[i + 1];
6562 bitmap[i + 2] = pu8Data[i];
6563 bitmap[i + 3] = 0xff;
6564 }
6565 }
6566 bitmap.detachTo(ComSafeArrayOutArg(aData));
6567
6568 freeSavedDisplayScreenshot(pu8Data);
6569
6570 return S_OK;
6571}
6572
6573
6574STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6575{
6576 LogFlowThisFunc(("\n"));
6577
6578 CheckComArgNotNull(aWidth);
6579 CheckComArgNotNull(aHeight);
6580 CheckComArgOutSafeArrayPointerValid(aData);
6581
6582 if (aScreenId != 0)
6583 return E_NOTIMPL;
6584
6585 AutoCaller autoCaller(this);
6586 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6587
6588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 uint8_t *pu8Data = NULL;
6591 uint32_t cbData = 0;
6592 uint32_t u32Width = 0;
6593 uint32_t u32Height = 0;
6594
6595 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6596
6597 if (RT_FAILURE(vrc))
6598 return setError(VBOX_E_IPRT_ERROR,
6599 tr("Saved screenshot data is not available (%Rrc)"),
6600 vrc);
6601
6602 *aWidth = u32Width;
6603 *aHeight = u32Height;
6604
6605 HRESULT rc = S_OK;
6606 uint8_t *pu8PNG = NULL;
6607 uint32_t cbPNG = 0;
6608 uint32_t cxPNG = 0;
6609 uint32_t cyPNG = 0;
6610
6611 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6612
6613 if (RT_SUCCESS(vrc))
6614 {
6615 com::SafeArray<BYTE> screenData(cbPNG);
6616 screenData.initFrom(pu8PNG, cbPNG);
6617 if (pu8PNG)
6618 RTMemFree(pu8PNG);
6619 screenData.detachTo(ComSafeArrayOutArg(aData));
6620 }
6621 else
6622 {
6623 if (pu8PNG)
6624 RTMemFree(pu8PNG);
6625 return setError(VBOX_E_IPRT_ERROR,
6626 tr("Could not convert screenshot to PNG (%Rrc)"),
6627 vrc);
6628 }
6629
6630 freeSavedDisplayScreenshot(pu8Data);
6631
6632 return rc;
6633}
6634
6635STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6636{
6637 LogFlowThisFunc(("\n"));
6638
6639 CheckComArgNotNull(aSize);
6640 CheckComArgNotNull(aWidth);
6641 CheckComArgNotNull(aHeight);
6642
6643 if (aScreenId != 0)
6644 return E_NOTIMPL;
6645
6646 AutoCaller autoCaller(this);
6647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6648
6649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6650
6651 uint8_t *pu8Data = NULL;
6652 uint32_t cbData = 0;
6653 uint32_t u32Width = 0;
6654 uint32_t u32Height = 0;
6655
6656 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6657
6658 if (RT_FAILURE(vrc))
6659 return setError(VBOX_E_IPRT_ERROR,
6660 tr("Saved screenshot data is not available (%Rrc)"),
6661 vrc);
6662
6663 *aSize = cbData;
6664 *aWidth = u32Width;
6665 *aHeight = u32Height;
6666
6667 freeSavedDisplayScreenshot(pu8Data);
6668
6669 return S_OK;
6670}
6671
6672STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6673{
6674 LogFlowThisFunc(("\n"));
6675
6676 CheckComArgNotNull(aWidth);
6677 CheckComArgNotNull(aHeight);
6678 CheckComArgOutSafeArrayPointerValid(aData);
6679
6680 if (aScreenId != 0)
6681 return E_NOTIMPL;
6682
6683 AutoCaller autoCaller(this);
6684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6685
6686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6687
6688 uint8_t *pu8Data = NULL;
6689 uint32_t cbData = 0;
6690 uint32_t u32Width = 0;
6691 uint32_t u32Height = 0;
6692
6693 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6694
6695 if (RT_FAILURE(vrc))
6696 return setError(VBOX_E_IPRT_ERROR,
6697 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6698 vrc);
6699
6700 *aWidth = u32Width;
6701 *aHeight = u32Height;
6702
6703 com::SafeArray<BYTE> png(cbData);
6704 png.initFrom(pu8Data, cbData);
6705 png.detachTo(ComSafeArrayOutArg(aData));
6706
6707 freeSavedDisplayScreenshot(pu8Data);
6708
6709 return S_OK;
6710}
6711
6712STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6713{
6714 HRESULT rc = S_OK;
6715 LogFlowThisFunc(("\n"));
6716
6717 AutoCaller autoCaller(this);
6718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6719
6720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6721
6722 if (!mHWData->mCPUHotPlugEnabled)
6723 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6724
6725 if (aCpu >= mHWData->mCPUCount)
6726 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6727
6728 if (mHWData->mCPUAttached[aCpu])
6729 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6730
6731 alock.release();
6732 rc = onCPUChange(aCpu, false);
6733 alock.acquire();
6734 if (FAILED(rc)) return rc;
6735
6736 setModified(IsModified_MachineData);
6737 mHWData.backup();
6738 mHWData->mCPUAttached[aCpu] = true;
6739
6740 /* Save settings if online */
6741 if (Global::IsOnline(mData->mMachineState))
6742 saveSettings(NULL);
6743
6744 return S_OK;
6745}
6746
6747STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6748{
6749 HRESULT rc = S_OK;
6750 LogFlowThisFunc(("\n"));
6751
6752 AutoCaller autoCaller(this);
6753 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6754
6755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6756
6757 if (!mHWData->mCPUHotPlugEnabled)
6758 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6759
6760 if (aCpu >= SchemaDefs::MaxCPUCount)
6761 return setError(E_INVALIDARG,
6762 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6763 SchemaDefs::MaxCPUCount);
6764
6765 if (!mHWData->mCPUAttached[aCpu])
6766 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6767
6768 /* CPU 0 can't be detached */
6769 if (aCpu == 0)
6770 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6771
6772 alock.release();
6773 rc = onCPUChange(aCpu, true);
6774 alock.acquire();
6775 if (FAILED(rc)) return rc;
6776
6777 setModified(IsModified_MachineData);
6778 mHWData.backup();
6779 mHWData->mCPUAttached[aCpu] = false;
6780
6781 /* Save settings if online */
6782 if (Global::IsOnline(mData->mMachineState))
6783 saveSettings(NULL);
6784
6785 return S_OK;
6786}
6787
6788STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6789{
6790 LogFlowThisFunc(("\n"));
6791
6792 CheckComArgNotNull(aCpuAttached);
6793
6794 *aCpuAttached = false;
6795
6796 AutoCaller autoCaller(this);
6797 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6798
6799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6800
6801 /* If hotplug is enabled the CPU is always enabled. */
6802 if (!mHWData->mCPUHotPlugEnabled)
6803 {
6804 if (aCpu < mHWData->mCPUCount)
6805 *aCpuAttached = true;
6806 }
6807 else
6808 {
6809 if (aCpu < SchemaDefs::MaxCPUCount)
6810 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6811 }
6812
6813 return S_OK;
6814}
6815
6816STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6817{
6818 CheckComArgOutPointerValid(aName);
6819
6820 AutoCaller autoCaller(this);
6821 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6822
6823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6824
6825 Utf8Str log = queryLogFilename(aIdx);
6826 if (!RTFileExists(log.c_str()))
6827 log.setNull();
6828 log.cloneTo(aName);
6829
6830 return S_OK;
6831}
6832
6833STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6834{
6835 LogFlowThisFunc(("\n"));
6836 CheckComArgOutSafeArrayPointerValid(aData);
6837 if (aSize < 0)
6838 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6839
6840 AutoCaller autoCaller(this);
6841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6842
6843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6844
6845 HRESULT rc = S_OK;
6846 Utf8Str log = queryLogFilename(aIdx);
6847
6848 /* do not unnecessarily hold the lock while doing something which does
6849 * not need the lock and potentially takes a long time. */
6850 alock.release();
6851
6852 /* Limit the chunk size to 32K for now, as that gives better performance
6853 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6854 * One byte expands to approx. 25 bytes of breathtaking XML. */
6855 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6856 com::SafeArray<BYTE> logData(cbData);
6857
6858 RTFILE LogFile;
6859 int vrc = RTFileOpen(&LogFile, log.c_str(),
6860 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6861 if (RT_SUCCESS(vrc))
6862 {
6863 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6864 if (RT_SUCCESS(vrc))
6865 logData.resize(cbData);
6866 else
6867 rc = setError(VBOX_E_IPRT_ERROR,
6868 tr("Could not read log file '%s' (%Rrc)"),
6869 log.c_str(), vrc);
6870 RTFileClose(LogFile);
6871 }
6872 else
6873 rc = setError(VBOX_E_IPRT_ERROR,
6874 tr("Could not open log file '%s' (%Rrc)"),
6875 log.c_str(), vrc);
6876
6877 if (FAILED(rc))
6878 logData.resize(0);
6879 logData.detachTo(ComSafeArrayOutArg(aData));
6880
6881 return rc;
6882}
6883
6884
6885/**
6886 * Currently this method doesn't attach device to the running VM,
6887 * just makes sure it's plugged on next VM start.
6888 */
6889STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6890{
6891 AutoCaller autoCaller(this);
6892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6893
6894 // lock scope
6895 {
6896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6897
6898 HRESULT rc = checkStateDependency(MutableStateDep);
6899 if (FAILED(rc)) return rc;
6900
6901 ChipsetType_T aChipset = ChipsetType_PIIX3;
6902 COMGETTER(ChipsetType)(&aChipset);
6903
6904 if (aChipset != ChipsetType_ICH9)
6905 {
6906 return setError(E_INVALIDARG,
6907 tr("Host PCI attachment only supported with ICH9 chipset"));
6908 }
6909
6910 // check if device with this host PCI address already attached
6911 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6912 it != mHWData->mPCIDeviceAssignments.end();
6913 ++it)
6914 {
6915 LONG iHostAddress = -1;
6916 ComPtr<PCIDeviceAttachment> pAttach;
6917 pAttach = *it;
6918 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6919 if (iHostAddress == hostAddress)
6920 return setError(E_INVALIDARG,
6921 tr("Device with host PCI address already attached to this VM"));
6922 }
6923
6924 ComObjPtr<PCIDeviceAttachment> pda;
6925 char name[32];
6926
6927 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6928 Bstr bname(name);
6929 pda.createObject();
6930 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6931 setModified(IsModified_MachineData);
6932 mHWData.backup();
6933 mHWData->mPCIDeviceAssignments.push_back(pda);
6934 }
6935
6936 return S_OK;
6937}
6938
6939/**
6940 * Currently this method doesn't detach device from the running VM,
6941 * just makes sure it's not plugged on next VM start.
6942 */
6943STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6944{
6945 AutoCaller autoCaller(this);
6946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6947
6948 ComObjPtr<PCIDeviceAttachment> pAttach;
6949 bool fRemoved = false;
6950 HRESULT rc;
6951
6952 // lock scope
6953 {
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955
6956 rc = checkStateDependency(MutableStateDep);
6957 if (FAILED(rc)) return rc;
6958
6959 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6960 it != mHWData->mPCIDeviceAssignments.end();
6961 ++it)
6962 {
6963 LONG iHostAddress = -1;
6964 pAttach = *it;
6965 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6966 if (iHostAddress != -1 && iHostAddress == hostAddress)
6967 {
6968 setModified(IsModified_MachineData);
6969 mHWData.backup();
6970 mHWData->mPCIDeviceAssignments.remove(pAttach);
6971 fRemoved = true;
6972 break;
6973 }
6974 }
6975 }
6976
6977
6978 /* Fire event outside of the lock */
6979 if (fRemoved)
6980 {
6981 Assert(!pAttach.isNull());
6982 ComPtr<IEventSource> es;
6983 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6984 Assert(SUCCEEDED(rc));
6985 Bstr mid;
6986 rc = this->COMGETTER(Id)(mid.asOutParam());
6987 Assert(SUCCEEDED(rc));
6988 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6989 }
6990
6991 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6992 tr("No host PCI device %08x attached"),
6993 hostAddress
6994 );
6995}
6996
6997STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6998{
6999 CheckComArgOutSafeArrayPointerValid(aAssignments);
7000
7001 AutoCaller autoCaller(this);
7002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7003
7004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7005
7006 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7007 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7008
7009 return S_OK;
7010}
7011
7012STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7013{
7014 CheckComArgOutPointerValid(aBandwidthControl);
7015
7016 AutoCaller autoCaller(this);
7017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7018
7019 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7020
7021 return S_OK;
7022}
7023
7024STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7025{
7026 CheckComArgOutPointerValid(pfEnabled);
7027 AutoCaller autoCaller(this);
7028 HRESULT hrc = autoCaller.rc();
7029 if (SUCCEEDED(hrc))
7030 {
7031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7032 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7033 }
7034 return hrc;
7035}
7036
7037STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7038{
7039 AutoCaller autoCaller(this);
7040 HRESULT hrc = autoCaller.rc();
7041 if (SUCCEEDED(hrc))
7042 {
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044 hrc = checkStateDependency(MutableStateDep);
7045 if (SUCCEEDED(hrc))
7046 {
7047 hrc = mHWData.backupEx();
7048 if (SUCCEEDED(hrc))
7049 {
7050 setModified(IsModified_MachineData);
7051 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7052 }
7053 }
7054 }
7055 return hrc;
7056}
7057
7058STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7059{
7060 CheckComArgOutPointerValid(pbstrConfig);
7061 AutoCaller autoCaller(this);
7062 HRESULT hrc = autoCaller.rc();
7063 if (SUCCEEDED(hrc))
7064 {
7065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7066 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7067 }
7068 return hrc;
7069}
7070
7071STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7072{
7073 CheckComArgStr(bstrConfig);
7074 AutoCaller autoCaller(this);
7075 HRESULT hrc = autoCaller.rc();
7076 if (SUCCEEDED(hrc))
7077 {
7078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7079 hrc = checkStateDependency(MutableStateDep);
7080 if (SUCCEEDED(hrc))
7081 {
7082 hrc = mHWData.backupEx();
7083 if (SUCCEEDED(hrc))
7084 {
7085 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7086 if (SUCCEEDED(hrc))
7087 setModified(IsModified_MachineData);
7088 }
7089 }
7090 }
7091 return hrc;
7092
7093}
7094
7095STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7096{
7097 CheckComArgOutPointerValid(pfAllow);
7098 AutoCaller autoCaller(this);
7099 HRESULT hrc = autoCaller.rc();
7100 if (SUCCEEDED(hrc))
7101 {
7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7103 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7104 }
7105 return hrc;
7106}
7107
7108STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7109{
7110 AutoCaller autoCaller(this);
7111 HRESULT hrc = autoCaller.rc();
7112 if (SUCCEEDED(hrc))
7113 {
7114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7115 hrc = checkStateDependency(MutableStateDep);
7116 if (SUCCEEDED(hrc))
7117 {
7118 hrc = mHWData.backupEx();
7119 if (SUCCEEDED(hrc))
7120 {
7121 setModified(IsModified_MachineData);
7122 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7123 }
7124 }
7125 }
7126 return hrc;
7127}
7128
7129STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7130{
7131 CheckComArgOutPointerValid(pfEnabled);
7132 AutoCaller autoCaller(this);
7133 HRESULT hrc = autoCaller.rc();
7134 if (SUCCEEDED(hrc))
7135 {
7136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7137 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7138 }
7139 return hrc;
7140}
7141
7142STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7143{
7144 AutoCaller autoCaller(this);
7145 HRESULT hrc = autoCaller.rc();
7146 if (SUCCEEDED(hrc))
7147 {
7148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7149 hrc = checkStateDependency(MutableStateDep);
7150 if ( SUCCEEDED(hrc)
7151 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7152 {
7153 AutostartDb *autostartDb = mParent->getAutostartDb();
7154 int vrc;
7155
7156 if (fEnabled)
7157 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7158 else
7159 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7160
7161 if (RT_SUCCESS(vrc))
7162 {
7163 hrc = mHWData.backupEx();
7164 if (SUCCEEDED(hrc))
7165 {
7166 setModified(IsModified_MachineData);
7167 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7168 }
7169 }
7170 else if (vrc == VERR_NOT_SUPPORTED)
7171 hrc = setError(VBOX_E_NOT_SUPPORTED,
7172 tr("The VM autostart feature is not supported on this platform"));
7173 else if (vrc == VERR_PATH_NOT_FOUND)
7174 hrc = setError(E_FAIL,
7175 tr("The path to the autostart database is not set"));
7176 else
7177 hrc = setError(E_UNEXPECTED,
7178 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7179 fEnabled ? "Adding" : "Removing",
7180 mUserData->s.strName.c_str(), vrc);
7181 }
7182 }
7183 return hrc;
7184}
7185
7186STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7187{
7188 CheckComArgOutPointerValid(puDelay);
7189 AutoCaller autoCaller(this);
7190 HRESULT hrc = autoCaller.rc();
7191 if (SUCCEEDED(hrc))
7192 {
7193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7194 *puDelay = mHWData->mAutostart.uAutostartDelay;
7195 }
7196 return hrc;
7197}
7198
7199STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7200{
7201 AutoCaller autoCaller(this);
7202 HRESULT hrc = autoCaller.rc();
7203 if (SUCCEEDED(hrc))
7204 {
7205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7206 hrc = checkStateDependency(MutableStateDep);
7207 if (SUCCEEDED(hrc))
7208 {
7209 hrc = mHWData.backupEx();
7210 if (SUCCEEDED(hrc))
7211 {
7212 setModified(IsModified_MachineData);
7213 mHWData->mAutostart.uAutostartDelay = uDelay;
7214 }
7215 }
7216 }
7217 return hrc;
7218}
7219
7220STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7221{
7222 CheckComArgOutPointerValid(penmAutostopType);
7223 AutoCaller autoCaller(this);
7224 HRESULT hrc = autoCaller.rc();
7225 if (SUCCEEDED(hrc))
7226 {
7227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7228 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7229 }
7230 return hrc;
7231}
7232
7233STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7234{
7235 AutoCaller autoCaller(this);
7236 HRESULT hrc = autoCaller.rc();
7237 if (SUCCEEDED(hrc))
7238 {
7239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7240 hrc = checkStateDependency(MutableStateDep);
7241 if ( SUCCEEDED(hrc)
7242 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7243 {
7244 AutostartDb *autostartDb = mParent->getAutostartDb();
7245 int vrc;
7246
7247 if (enmAutostopType != AutostopType_Disabled)
7248 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7249 else
7250 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7251
7252 if (RT_SUCCESS(vrc))
7253 {
7254 hrc = mHWData.backupEx();
7255 if (SUCCEEDED(hrc))
7256 {
7257 setModified(IsModified_MachineData);
7258 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7259 }
7260 }
7261 else if (vrc == VERR_NOT_SUPPORTED)
7262 hrc = setError(VBOX_E_NOT_SUPPORTED,
7263 tr("The VM autostop feature is not supported on this platform"));
7264 else if (vrc == VERR_PATH_NOT_FOUND)
7265 hrc = setError(E_FAIL,
7266 tr("The path to the autostart database is not set"));
7267 else
7268 hrc = setError(E_UNEXPECTED,
7269 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7270 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7271 mUserData->s.strName.c_str(), vrc);
7272 }
7273 }
7274 return hrc;
7275}
7276
7277STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7278{
7279 CheckComArgOutPointerValid(aDefaultFrontend);
7280 AutoCaller autoCaller(this);
7281 HRESULT hrc = autoCaller.rc();
7282 if (SUCCEEDED(hrc))
7283 {
7284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7285 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7286 }
7287 return hrc;
7288}
7289
7290STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7291{
7292 CheckComArgStr(aDefaultFrontend);
7293 AutoCaller autoCaller(this);
7294 HRESULT hrc = autoCaller.rc();
7295 if (SUCCEEDED(hrc))
7296 {
7297 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7298 hrc = checkStateDependency(MutableOrSavedStateDep);
7299 if (SUCCEEDED(hrc))
7300 {
7301 hrc = mHWData.backupEx();
7302 if (SUCCEEDED(hrc))
7303 {
7304 setModified(IsModified_MachineData);
7305 mHWData->mDefaultFrontend = aDefaultFrontend;
7306 }
7307 }
7308 }
7309 return hrc;
7310}
7311
7312STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7313{
7314 CheckComArgSafeArrayNotNull(aIcon);
7315 CheckComArgOutSafeArrayPointerValid(aIcon);
7316 AutoCaller autoCaller(this);
7317 HRESULT hrc = autoCaller.rc();
7318 if (SUCCEEDED(hrc))
7319 {
7320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7321 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7322 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7323 icon.detachTo(ComSafeArrayOutArg(aIcon));
7324 }
7325 return hrc;
7326}
7327
7328STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7329{
7330 CheckComArgSafeArrayNotNull(aIcon);
7331 AutoCaller autoCaller(this);
7332 HRESULT hrc = autoCaller.rc();
7333 if (SUCCEEDED(hrc))
7334 {
7335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7336 hrc = checkStateDependency(MutableOrSavedStateDep);
7337 if (SUCCEEDED(hrc))
7338 {
7339 setModified(IsModified_MachineData);
7340 mUserData.backup();
7341 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7342 mUserData->mIcon.clear();
7343 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7344 }
7345 }
7346 return hrc;
7347}
7348
7349STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7350{
7351 LogFlowFuncEnter();
7352
7353 CheckComArgNotNull(pTarget);
7354 CheckComArgOutPointerValid(pProgress);
7355
7356 /* Convert the options. */
7357 RTCList<CloneOptions_T> optList;
7358 if (options != NULL)
7359 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7360
7361 if (optList.contains(CloneOptions_Link))
7362 {
7363 if (!isSnapshotMachine())
7364 return setError(E_INVALIDARG,
7365 tr("Linked clone can only be created from a snapshot"));
7366 if (mode != CloneMode_MachineState)
7367 return setError(E_INVALIDARG,
7368 tr("Linked clone can only be created for a single machine state"));
7369 }
7370 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7371
7372 AutoCaller autoCaller(this);
7373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7374
7375
7376 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7377
7378 HRESULT rc = pWorker->start(pProgress);
7379
7380 LogFlowFuncLeave();
7381
7382 return rc;
7383}
7384
7385// public methods for internal purposes
7386/////////////////////////////////////////////////////////////////////////////
7387
7388/**
7389 * Adds the given IsModified_* flag to the dirty flags of the machine.
7390 * This must be called either during loadSettings or under the machine write lock.
7391 * @param fl
7392 */
7393void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7394{
7395 mData->flModifications |= fl;
7396 if (fAllowStateModification && isStateModificationAllowed())
7397 mData->mCurrentStateModified = true;
7398}
7399
7400/**
7401 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7402 * care of the write locking.
7403 *
7404 * @param fModifications The flag to add.
7405 */
7406void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7407{
7408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7409 setModified(fModification, fAllowStateModification);
7410}
7411
7412/**
7413 * Saves the registry entry of this machine to the given configuration node.
7414 *
7415 * @param aEntryNode Node to save the registry entry to.
7416 *
7417 * @note locks this object for reading.
7418 */
7419HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7420{
7421 AutoLimitedCaller autoCaller(this);
7422 AssertComRCReturnRC(autoCaller.rc());
7423
7424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7425
7426 data.uuid = mData->mUuid;
7427 data.strSettingsFile = mData->m_strConfigFile;
7428
7429 return S_OK;
7430}
7431
7432/**
7433 * Calculates the absolute path of the given path taking the directory of the
7434 * machine settings file as the current directory.
7435 *
7436 * @param aPath Path to calculate the absolute path for.
7437 * @param aResult Where to put the result (used only on success, can be the
7438 * same Utf8Str instance as passed in @a aPath).
7439 * @return IPRT result.
7440 *
7441 * @note Locks this object for reading.
7442 */
7443int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7444{
7445 AutoCaller autoCaller(this);
7446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7447
7448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7449
7450 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7451
7452 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7453
7454 strSettingsDir.stripFilename();
7455 char folder[RTPATH_MAX];
7456 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7457 if (RT_SUCCESS(vrc))
7458 aResult = folder;
7459
7460 return vrc;
7461}
7462
7463/**
7464 * Copies strSource to strTarget, making it relative to the machine folder
7465 * if it is a subdirectory thereof, or simply copying it otherwise.
7466 *
7467 * @param strSource Path to evaluate and copy.
7468 * @param strTarget Buffer to receive target path.
7469 *
7470 * @note Locks this object for reading.
7471 */
7472void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7473 Utf8Str &strTarget)
7474{
7475 AutoCaller autoCaller(this);
7476 AssertComRCReturn(autoCaller.rc(), (void)0);
7477
7478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7479
7480 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7481 // use strTarget as a temporary buffer to hold the machine settings dir
7482 strTarget = mData->m_strConfigFileFull;
7483 strTarget.stripFilename();
7484 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7485 {
7486 // is relative: then append what's left
7487 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7488 // for empty paths (only possible for subdirs) use "." to avoid
7489 // triggering default settings for not present config attributes.
7490 if (strTarget.isEmpty())
7491 strTarget = ".";
7492 }
7493 else
7494 // is not relative: then overwrite
7495 strTarget = strSource;
7496}
7497
7498/**
7499 * Returns the full path to the machine's log folder in the
7500 * \a aLogFolder argument.
7501 */
7502void Machine::getLogFolder(Utf8Str &aLogFolder)
7503{
7504 AutoCaller autoCaller(this);
7505 AssertComRCReturnVoid(autoCaller.rc());
7506
7507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7508
7509 char szTmp[RTPATH_MAX];
7510 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7511 if (RT_SUCCESS(vrc))
7512 {
7513 if (szTmp[0] && !mUserData.isNull())
7514 {
7515 char szTmp2[RTPATH_MAX];
7516 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7517 if (RT_SUCCESS(vrc))
7518 aLogFolder = BstrFmt("%s%c%s",
7519 szTmp2,
7520 RTPATH_DELIMITER,
7521 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7522 }
7523 else
7524 vrc = VERR_PATH_IS_RELATIVE;
7525 }
7526
7527 if (RT_FAILURE(vrc))
7528 {
7529 // fallback if VBOX_USER_LOGHOME is not set or invalid
7530 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7531 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7532 aLogFolder.append(RTPATH_DELIMITER);
7533 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7534 }
7535}
7536
7537/**
7538 * Returns the full path to the machine's log file for an given index.
7539 */
7540Utf8Str Machine::queryLogFilename(ULONG idx)
7541{
7542 Utf8Str logFolder;
7543 getLogFolder(logFolder);
7544 Assert(logFolder.length());
7545 Utf8Str log;
7546 if (idx == 0)
7547 log = Utf8StrFmt("%s%cVBox.log",
7548 logFolder.c_str(), RTPATH_DELIMITER);
7549 else
7550 log = Utf8StrFmt("%s%cVBox.log.%d",
7551 logFolder.c_str(), RTPATH_DELIMITER, idx);
7552 return log;
7553}
7554
7555/**
7556 * Composes a unique saved state filename based on the current system time. The filename is
7557 * granular to the second so this will work so long as no more than one snapshot is taken on
7558 * a machine per second.
7559 *
7560 * Before version 4.1, we used this formula for saved state files:
7561 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7562 * which no longer works because saved state files can now be shared between the saved state of the
7563 * "saved" machine and an online snapshot, and the following would cause problems:
7564 * 1) save machine
7565 * 2) create online snapshot from that machine state --> reusing saved state file
7566 * 3) save machine again --> filename would be reused, breaking the online snapshot
7567 *
7568 * So instead we now use a timestamp.
7569 *
7570 * @param str
7571 */
7572void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7573{
7574 AutoCaller autoCaller(this);
7575 AssertComRCReturnVoid(autoCaller.rc());
7576
7577 {
7578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7579 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7580 }
7581
7582 RTTIMESPEC ts;
7583 RTTimeNow(&ts);
7584 RTTIME time;
7585 RTTimeExplode(&time, &ts);
7586
7587 strStateFilePath += RTPATH_DELIMITER;
7588 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7589 time.i32Year, time.u8Month, time.u8MonthDay,
7590 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7591}
7592
7593/**
7594 * @note Locks this object for writing, calls the client process
7595 * (inside the lock).
7596 */
7597HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7598 const Utf8Str &strFrontend,
7599 const Utf8Str &strEnvironment,
7600 ProgressProxy *aProgress)
7601{
7602 LogFlowThisFuncEnter();
7603
7604 AssertReturn(aControl, E_FAIL);
7605 AssertReturn(aProgress, E_FAIL);
7606 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7607
7608 AutoCaller autoCaller(this);
7609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7610
7611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7612
7613 if (!mData->mRegistered)
7614 return setError(E_UNEXPECTED,
7615 tr("The machine '%s' is not registered"),
7616 mUserData->s.strName.c_str());
7617
7618 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7619
7620 if ( mData->mSession.mState == SessionState_Locked
7621 || mData->mSession.mState == SessionState_Spawning
7622 || mData->mSession.mState == SessionState_Unlocking)
7623 return setError(VBOX_E_INVALID_OBJECT_STATE,
7624 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7625 mUserData->s.strName.c_str());
7626
7627 /* may not be busy */
7628 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7629
7630 /* get the path to the executable */
7631 char szPath[RTPATH_MAX];
7632 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7633 size_t sz = strlen(szPath);
7634 szPath[sz++] = RTPATH_DELIMITER;
7635 szPath[sz] = 0;
7636 char *cmd = szPath + sz;
7637 sz = RTPATH_MAX - sz;
7638
7639 int vrc = VINF_SUCCESS;
7640 RTPROCESS pid = NIL_RTPROCESS;
7641
7642 RTENV env = RTENV_DEFAULT;
7643
7644 if (!strEnvironment.isEmpty())
7645 {
7646 char *newEnvStr = NULL;
7647
7648 do
7649 {
7650 /* clone the current environment */
7651 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7652 AssertRCBreakStmt(vrc2, vrc = vrc2);
7653
7654 newEnvStr = RTStrDup(strEnvironment.c_str());
7655 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7656
7657 /* put new variables to the environment
7658 * (ignore empty variable names here since RTEnv API
7659 * intentionally doesn't do that) */
7660 char *var = newEnvStr;
7661 for (char *p = newEnvStr; *p; ++p)
7662 {
7663 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7664 {
7665 *p = '\0';
7666 if (*var)
7667 {
7668 char *val = strchr(var, '=');
7669 if (val)
7670 {
7671 *val++ = '\0';
7672 vrc2 = RTEnvSetEx(env, var, val);
7673 }
7674 else
7675 vrc2 = RTEnvUnsetEx(env, var);
7676 if (RT_FAILURE(vrc2))
7677 break;
7678 }
7679 var = p + 1;
7680 }
7681 }
7682 if (RT_SUCCESS(vrc2) && *var)
7683 vrc2 = RTEnvPutEx(env, var);
7684
7685 AssertRCBreakStmt(vrc2, vrc = vrc2);
7686 }
7687 while (0);
7688
7689 if (newEnvStr != NULL)
7690 RTStrFree(newEnvStr);
7691 }
7692
7693#ifdef VBOX_WITH_QTGUI
7694 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7695 {
7696# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7697 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7698# else
7699 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7700# endif
7701 Assert(sz >= sizeof(VirtualBox_exe));
7702 strcpy(cmd, VirtualBox_exe);
7703
7704 Utf8Str idStr = mData->mUuid.toString();
7705 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7706 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7707 }
7708#else /* !VBOX_WITH_QTGUI */
7709 if (0)
7710 ;
7711#endif /* VBOX_WITH_QTGUI */
7712
7713 else
7714
7715#ifdef VBOX_WITH_VBOXSDL
7716 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7717 {
7718 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7719 Assert(sz >= sizeof(VBoxSDL_exe));
7720 strcpy(cmd, VBoxSDL_exe);
7721
7722 Utf8Str idStr = mData->mUuid.toString();
7723 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7724 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7725 }
7726#else /* !VBOX_WITH_VBOXSDL */
7727 if (0)
7728 ;
7729#endif /* !VBOX_WITH_VBOXSDL */
7730
7731 else
7732
7733#ifdef VBOX_WITH_HEADLESS
7734 if ( strFrontend == "headless"
7735 || strFrontend == "capture"
7736 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7737 )
7738 {
7739 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7740 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7741 * and a VM works even if the server has not been installed.
7742 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7743 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7744 * differently in 4.0 and 3.x.
7745 */
7746 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7747 Assert(sz >= sizeof(VBoxHeadless_exe));
7748 strcpy(cmd, VBoxHeadless_exe);
7749
7750 Utf8Str idStr = mData->mUuid.toString();
7751 /* Leave space for "--capture" arg. */
7752 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7753 "--startvm", idStr.c_str(),
7754 "--vrde", "config",
7755 0, /* For "--capture". */
7756 0 };
7757 if (strFrontend == "capture")
7758 {
7759 unsigned pos = RT_ELEMENTS(args) - 2;
7760 args[pos] = "--capture";
7761 }
7762 vrc = RTProcCreate(szPath, args, env,
7763#ifdef RT_OS_WINDOWS
7764 RTPROC_FLAGS_NO_WINDOW
7765#else
7766 0
7767#endif
7768 , &pid);
7769 }
7770#else /* !VBOX_WITH_HEADLESS */
7771 if (0)
7772 ;
7773#endif /* !VBOX_WITH_HEADLESS */
7774 else
7775 {
7776 RTEnvDestroy(env);
7777 return setError(E_INVALIDARG,
7778 tr("Invalid frontend name: '%s'"),
7779 strFrontend.c_str());
7780 }
7781
7782 RTEnvDestroy(env);
7783
7784 if (RT_FAILURE(vrc))
7785 return setError(VBOX_E_IPRT_ERROR,
7786 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7787 mUserData->s.strName.c_str(), vrc);
7788
7789 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7790
7791 /*
7792 * Note that we don't release the lock here before calling the client,
7793 * because it doesn't need to call us back if called with a NULL argument.
7794 * Releasing the lock here is dangerous because we didn't prepare the
7795 * launch data yet, but the client we've just started may happen to be
7796 * too fast and call openSession() that will fail (because of PID, etc.),
7797 * so that the Machine will never get out of the Spawning session state.
7798 */
7799
7800 /* inform the session that it will be a remote one */
7801 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7802 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7803 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7804
7805 if (FAILED(rc))
7806 {
7807 /* restore the session state */
7808 mData->mSession.mState = SessionState_Unlocked;
7809 /* The failure may occur w/o any error info (from RPC), so provide one */
7810 return setError(VBOX_E_VM_ERROR,
7811 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7812 }
7813
7814 /* attach launch data to the machine */
7815 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7816 mData->mSession.mRemoteControls.push_back(aControl);
7817 mData->mSession.mProgress = aProgress;
7818 mData->mSession.mPID = pid;
7819 mData->mSession.mState = SessionState_Spawning;
7820 mData->mSession.mType = strFrontend;
7821
7822 LogFlowThisFuncLeave();
7823 return S_OK;
7824}
7825
7826/**
7827 * Returns @c true if the given machine has an open direct session and returns
7828 * the session machine instance and additional session data (on some platforms)
7829 * if so.
7830 *
7831 * Note that when the method returns @c false, the arguments remain unchanged.
7832 *
7833 * @param aMachine Session machine object.
7834 * @param aControl Direct session control object (optional).
7835 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7836 *
7837 * @note locks this object for reading.
7838 */
7839#if defined(RT_OS_WINDOWS)
7840bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7841 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7842 HANDLE *aIPCSem /*= NULL*/,
7843 bool aAllowClosing /*= false*/)
7844#elif defined(RT_OS_OS2)
7845bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7846 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7847 HMTX *aIPCSem /*= NULL*/,
7848 bool aAllowClosing /*= false*/)
7849#else
7850bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7851 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7852 bool aAllowClosing /*= false*/)
7853#endif
7854{
7855 AutoLimitedCaller autoCaller(this);
7856 AssertComRCReturn(autoCaller.rc(), false);
7857
7858 /* just return false for inaccessible machines */
7859 if (autoCaller.state() != Ready)
7860 return false;
7861
7862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7863
7864 if ( mData->mSession.mState == SessionState_Locked
7865 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7866 )
7867 {
7868 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7869
7870 aMachine = mData->mSession.mMachine;
7871
7872 if (aControl != NULL)
7873 *aControl = mData->mSession.mDirectControl;
7874
7875#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7876 /* Additional session data */
7877 if (aIPCSem != NULL)
7878 *aIPCSem = aMachine->mIPCSem;
7879#endif
7880 return true;
7881 }
7882
7883 return false;
7884}
7885
7886/**
7887 * Returns @c true if the given machine has an spawning direct session and
7888 * returns and additional session data (on some platforms) if so.
7889 *
7890 * Note that when the method returns @c false, the arguments remain unchanged.
7891 *
7892 * @param aPID PID of the spawned direct session process.
7893 *
7894 * @note locks this object for reading.
7895 */
7896#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7897bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7898#else
7899bool Machine::isSessionSpawning()
7900#endif
7901{
7902 AutoLimitedCaller autoCaller(this);
7903 AssertComRCReturn(autoCaller.rc(), false);
7904
7905 /* just return false for inaccessible machines */
7906 if (autoCaller.state() != Ready)
7907 return false;
7908
7909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7910
7911 if (mData->mSession.mState == SessionState_Spawning)
7912 {
7913#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7914 /* Additional session data */
7915 if (aPID != NULL)
7916 {
7917 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7918 *aPID = mData->mSession.mPID;
7919 }
7920#endif
7921 return true;
7922 }
7923
7924 return false;
7925}
7926
7927/**
7928 * Called from the client watcher thread to check for unexpected client process
7929 * death during Session_Spawning state (e.g. before it successfully opened a
7930 * direct session).
7931 *
7932 * On Win32 and on OS/2, this method is called only when we've got the
7933 * direct client's process termination notification, so it always returns @c
7934 * true.
7935 *
7936 * On other platforms, this method returns @c true if the client process is
7937 * terminated and @c false if it's still alive.
7938 *
7939 * @note Locks this object for writing.
7940 */
7941bool Machine::checkForSpawnFailure()
7942{
7943 AutoCaller autoCaller(this);
7944 if (!autoCaller.isOk())
7945 {
7946 /* nothing to do */
7947 LogFlowThisFunc(("Already uninitialized!\n"));
7948 return true;
7949 }
7950
7951 /* VirtualBox::addProcessToReap() needs a write lock */
7952 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7953
7954 if (mData->mSession.mState != SessionState_Spawning)
7955 {
7956 /* nothing to do */
7957 LogFlowThisFunc(("Not spawning any more!\n"));
7958 return true;
7959 }
7960
7961 HRESULT rc = S_OK;
7962
7963#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7964
7965 /* the process was already unexpectedly terminated, we just need to set an
7966 * error and finalize session spawning */
7967 rc = setError(E_FAIL,
7968 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7969 getName().c_str());
7970#else
7971
7972 /* PID not yet initialized, skip check. */
7973 if (mData->mSession.mPID == NIL_RTPROCESS)
7974 return false;
7975
7976 RTPROCSTATUS status;
7977 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7978 &status);
7979
7980 if (vrc != VERR_PROCESS_RUNNING)
7981 {
7982 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7983 rc = setError(E_FAIL,
7984 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7985 getName().c_str(), status.iStatus);
7986 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7987 rc = setError(E_FAIL,
7988 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7989 getName().c_str(), status.iStatus);
7990 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7991 rc = setError(E_FAIL,
7992 tr("The virtual machine '%s' has terminated abnormally"),
7993 getName().c_str(), status.iStatus);
7994 else
7995 rc = setError(E_FAIL,
7996 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7997 getName().c_str(), rc);
7998 }
7999
8000#endif
8001
8002 if (FAILED(rc))
8003 {
8004 /* Close the remote session, remove the remote control from the list
8005 * and reset session state to Closed (@note keep the code in sync with
8006 * the relevant part in checkForSpawnFailure()). */
8007
8008 Assert(mData->mSession.mRemoteControls.size() == 1);
8009 if (mData->mSession.mRemoteControls.size() == 1)
8010 {
8011 ErrorInfoKeeper eik;
8012 mData->mSession.mRemoteControls.front()->Uninitialize();
8013 }
8014
8015 mData->mSession.mRemoteControls.clear();
8016 mData->mSession.mState = SessionState_Unlocked;
8017
8018 /* finalize the progress after setting the state */
8019 if (!mData->mSession.mProgress.isNull())
8020 {
8021 mData->mSession.mProgress->notifyComplete(rc);
8022 mData->mSession.mProgress.setNull();
8023 }
8024
8025 mParent->addProcessToReap(mData->mSession.mPID);
8026 mData->mSession.mPID = NIL_RTPROCESS;
8027
8028 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8029 return true;
8030 }
8031
8032 return false;
8033}
8034
8035/**
8036 * Checks whether the machine can be registered. If so, commits and saves
8037 * all settings.
8038 *
8039 * @note Must be called from mParent's write lock. Locks this object and
8040 * children for writing.
8041 */
8042HRESULT Machine::prepareRegister()
8043{
8044 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8045
8046 AutoLimitedCaller autoCaller(this);
8047 AssertComRCReturnRC(autoCaller.rc());
8048
8049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8050
8051 /* wait for state dependents to drop to zero */
8052 ensureNoStateDependencies();
8053
8054 if (!mData->mAccessible)
8055 return setError(VBOX_E_INVALID_OBJECT_STATE,
8056 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8057 mUserData->s.strName.c_str(),
8058 mData->mUuid.toString().c_str());
8059
8060 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8061
8062 if (mData->mRegistered)
8063 return setError(VBOX_E_INVALID_OBJECT_STATE,
8064 tr("The machine '%s' with UUID {%s} is already registered"),
8065 mUserData->s.strName.c_str(),
8066 mData->mUuid.toString().c_str());
8067
8068 HRESULT rc = S_OK;
8069
8070 // Ensure the settings are saved. If we are going to be registered and
8071 // no config file exists yet, create it by calling saveSettings() too.
8072 if ( (mData->flModifications)
8073 || (!mData->pMachineConfigFile->fileExists())
8074 )
8075 {
8076 rc = saveSettings(NULL);
8077 // no need to check whether VirtualBox.xml needs saving too since
8078 // we can't have a machine XML file rename pending
8079 if (FAILED(rc)) return rc;
8080 }
8081
8082 /* more config checking goes here */
8083
8084 if (SUCCEEDED(rc))
8085 {
8086 /* we may have had implicit modifications we want to fix on success */
8087 commit();
8088
8089 mData->mRegistered = true;
8090 }
8091 else
8092 {
8093 /* we may have had implicit modifications we want to cancel on failure*/
8094 rollback(false /* aNotify */);
8095 }
8096
8097 return rc;
8098}
8099
8100/**
8101 * Increases the number of objects dependent on the machine state or on the
8102 * registered state. Guarantees that these two states will not change at least
8103 * until #releaseStateDependency() is called.
8104 *
8105 * Depending on the @a aDepType value, additional state checks may be made.
8106 * These checks will set extended error info on failure. See
8107 * #checkStateDependency() for more info.
8108 *
8109 * If this method returns a failure, the dependency is not added and the caller
8110 * is not allowed to rely on any particular machine state or registration state
8111 * value and may return the failed result code to the upper level.
8112 *
8113 * @param aDepType Dependency type to add.
8114 * @param aState Current machine state (NULL if not interested).
8115 * @param aRegistered Current registered state (NULL if not interested).
8116 *
8117 * @note Locks this object for writing.
8118 */
8119HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8120 MachineState_T *aState /* = NULL */,
8121 BOOL *aRegistered /* = NULL */)
8122{
8123 AutoCaller autoCaller(this);
8124 AssertComRCReturnRC(autoCaller.rc());
8125
8126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8127
8128 HRESULT rc = checkStateDependency(aDepType);
8129 if (FAILED(rc)) return rc;
8130
8131 {
8132 if (mData->mMachineStateChangePending != 0)
8133 {
8134 /* ensureNoStateDependencies() is waiting for state dependencies to
8135 * drop to zero so don't add more. It may make sense to wait a bit
8136 * and retry before reporting an error (since the pending state
8137 * transition should be really quick) but let's just assert for
8138 * now to see if it ever happens on practice. */
8139
8140 AssertFailed();
8141
8142 return setError(E_ACCESSDENIED,
8143 tr("Machine state change is in progress. Please retry the operation later."));
8144 }
8145
8146 ++mData->mMachineStateDeps;
8147 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8148 }
8149
8150 if (aState)
8151 *aState = mData->mMachineState;
8152 if (aRegistered)
8153 *aRegistered = mData->mRegistered;
8154
8155 return S_OK;
8156}
8157
8158/**
8159 * Decreases the number of objects dependent on the machine state.
8160 * Must always complete the #addStateDependency() call after the state
8161 * dependency is no more necessary.
8162 */
8163void Machine::releaseStateDependency()
8164{
8165 AutoCaller autoCaller(this);
8166 AssertComRCReturnVoid(autoCaller.rc());
8167
8168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8169
8170 /* releaseStateDependency() w/o addStateDependency()? */
8171 AssertReturnVoid(mData->mMachineStateDeps != 0);
8172 -- mData->mMachineStateDeps;
8173
8174 if (mData->mMachineStateDeps == 0)
8175 {
8176 /* inform ensureNoStateDependencies() that there are no more deps */
8177 if (mData->mMachineStateChangePending != 0)
8178 {
8179 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8180 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8181 }
8182 }
8183}
8184
8185Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8186{
8187 /* start with nothing found */
8188 Utf8Str strResult("");
8189
8190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8191
8192 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8193 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8194 // found:
8195 strResult = it->second; // source is a Utf8Str
8196
8197 return strResult;
8198}
8199
8200// protected methods
8201/////////////////////////////////////////////////////////////////////////////
8202
8203/**
8204 * Performs machine state checks based on the @a aDepType value. If a check
8205 * fails, this method will set extended error info, otherwise it will return
8206 * S_OK. It is supposed, that on failure, the caller will immediately return
8207 * the return value of this method to the upper level.
8208 *
8209 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8210 *
8211 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8212 * current state of this machine object allows to change settings of the
8213 * machine (i.e. the machine is not registered, or registered but not running
8214 * and not saved). It is useful to call this method from Machine setters
8215 * before performing any change.
8216 *
8217 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8218 * as for MutableStateDep except that if the machine is saved, S_OK is also
8219 * returned. This is useful in setters which allow changing machine
8220 * properties when it is in the saved state.
8221 *
8222 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8223 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8224 * Aborted).
8225 *
8226 * @param aDepType Dependency type to check.
8227 *
8228 * @note Non Machine based classes should use #addStateDependency() and
8229 * #releaseStateDependency() methods or the smart AutoStateDependency
8230 * template.
8231 *
8232 * @note This method must be called from under this object's read or write
8233 * lock.
8234 */
8235HRESULT Machine::checkStateDependency(StateDependency aDepType)
8236{
8237 switch (aDepType)
8238 {
8239 case AnyStateDep:
8240 {
8241 break;
8242 }
8243 case MutableStateDep:
8244 {
8245 if ( mData->mRegistered
8246 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8247 || ( mData->mMachineState != MachineState_Paused
8248 && mData->mMachineState != MachineState_Running
8249 && mData->mMachineState != MachineState_Aborted
8250 && mData->mMachineState != MachineState_Teleported
8251 && mData->mMachineState != MachineState_PoweredOff
8252 )
8253 )
8254 )
8255 return setError(VBOX_E_INVALID_VM_STATE,
8256 tr("The machine is not mutable (state is %s)"),
8257 Global::stringifyMachineState(mData->mMachineState));
8258 break;
8259 }
8260 case MutableOrSavedStateDep:
8261 {
8262 if ( mData->mRegistered
8263 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8264 || ( mData->mMachineState != MachineState_Paused
8265 && mData->mMachineState != MachineState_Running
8266 && mData->mMachineState != MachineState_Aborted
8267 && mData->mMachineState != MachineState_Teleported
8268 && mData->mMachineState != MachineState_Saved
8269 && mData->mMachineState != MachineState_PoweredOff
8270 )
8271 )
8272 )
8273 return setError(VBOX_E_INVALID_VM_STATE,
8274 tr("The machine is not mutable (state is %s)"),
8275 Global::stringifyMachineState(mData->mMachineState));
8276 break;
8277 }
8278 case OfflineStateDep:
8279 {
8280 if ( mData->mRegistered
8281 && ( !isSessionMachine()
8282 || ( mData->mMachineState != MachineState_PoweredOff
8283 && mData->mMachineState != MachineState_Saved
8284 && mData->mMachineState != MachineState_Aborted
8285 && mData->mMachineState != MachineState_Teleported
8286 )
8287 )
8288 )
8289 return setError(VBOX_E_INVALID_VM_STATE,
8290 tr("The machine is not offline (state is %s)"),
8291 Global::stringifyMachineState(mData->mMachineState));
8292 break;
8293 }
8294 }
8295
8296 return S_OK;
8297}
8298
8299/**
8300 * Helper to initialize all associated child objects and allocate data
8301 * structures.
8302 *
8303 * This method must be called as a part of the object's initialization procedure
8304 * (usually done in the #init() method).
8305 *
8306 * @note Must be called only from #init() or from #registeredInit().
8307 */
8308HRESULT Machine::initDataAndChildObjects()
8309{
8310 AutoCaller autoCaller(this);
8311 AssertComRCReturnRC(autoCaller.rc());
8312 AssertComRCReturn(autoCaller.state() == InInit ||
8313 autoCaller.state() == Limited, E_FAIL);
8314
8315 AssertReturn(!mData->mAccessible, E_FAIL);
8316
8317 /* allocate data structures */
8318 mSSData.allocate();
8319 mUserData.allocate();
8320 mHWData.allocate();
8321 mMediaData.allocate();
8322 mStorageControllers.allocate();
8323
8324 /* initialize mOSTypeId */
8325 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8326
8327 /* create associated BIOS settings object */
8328 unconst(mBIOSSettings).createObject();
8329 mBIOSSettings->init(this);
8330
8331 /* create an associated VRDE object (default is disabled) */
8332 unconst(mVRDEServer).createObject();
8333 mVRDEServer->init(this);
8334
8335 /* create associated serial port objects */
8336 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8337 {
8338 unconst(mSerialPorts[slot]).createObject();
8339 mSerialPorts[slot]->init(this, slot);
8340 }
8341
8342 /* create associated parallel port objects */
8343 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8344 {
8345 unconst(mParallelPorts[slot]).createObject();
8346 mParallelPorts[slot]->init(this, slot);
8347 }
8348
8349 /* create the audio adapter object (always present, default is disabled) */
8350 unconst(mAudioAdapter).createObject();
8351 mAudioAdapter->init(this);
8352
8353 /* create the USB controller object (always present, default is disabled) */
8354 unconst(mUSBController).createObject();
8355 mUSBController->init(this);
8356
8357 /* create associated network adapter objects */
8358 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8359 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8360 {
8361 unconst(mNetworkAdapters[slot]).createObject();
8362 mNetworkAdapters[slot]->init(this, slot);
8363 }
8364
8365 /* create the bandwidth control */
8366 unconst(mBandwidthControl).createObject();
8367 mBandwidthControl->init(this);
8368
8369 return S_OK;
8370}
8371
8372/**
8373 * Helper to uninitialize all associated child objects and to free all data
8374 * structures.
8375 *
8376 * This method must be called as a part of the object's uninitialization
8377 * procedure (usually done in the #uninit() method).
8378 *
8379 * @note Must be called only from #uninit() or from #registeredInit().
8380 */
8381void Machine::uninitDataAndChildObjects()
8382{
8383 AutoCaller autoCaller(this);
8384 AssertComRCReturnVoid(autoCaller.rc());
8385 AssertComRCReturnVoid( autoCaller.state() == InUninit
8386 || autoCaller.state() == Limited);
8387
8388 /* tell all our other child objects we've been uninitialized */
8389 if (mBandwidthControl)
8390 {
8391 mBandwidthControl->uninit();
8392 unconst(mBandwidthControl).setNull();
8393 }
8394
8395 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8396 {
8397 if (mNetworkAdapters[slot])
8398 {
8399 mNetworkAdapters[slot]->uninit();
8400 unconst(mNetworkAdapters[slot]).setNull();
8401 }
8402 }
8403
8404 if (mUSBController)
8405 {
8406 mUSBController->uninit();
8407 unconst(mUSBController).setNull();
8408 }
8409
8410 if (mAudioAdapter)
8411 {
8412 mAudioAdapter->uninit();
8413 unconst(mAudioAdapter).setNull();
8414 }
8415
8416 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8417 {
8418 if (mParallelPorts[slot])
8419 {
8420 mParallelPorts[slot]->uninit();
8421 unconst(mParallelPorts[slot]).setNull();
8422 }
8423 }
8424
8425 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8426 {
8427 if (mSerialPorts[slot])
8428 {
8429 mSerialPorts[slot]->uninit();
8430 unconst(mSerialPorts[slot]).setNull();
8431 }
8432 }
8433
8434 if (mVRDEServer)
8435 {
8436 mVRDEServer->uninit();
8437 unconst(mVRDEServer).setNull();
8438 }
8439
8440 if (mBIOSSettings)
8441 {
8442 mBIOSSettings->uninit();
8443 unconst(mBIOSSettings).setNull();
8444 }
8445
8446 /* Deassociate media (only when a real Machine or a SnapshotMachine
8447 * instance is uninitialized; SessionMachine instances refer to real
8448 * Machine media). This is necessary for a clean re-initialization of
8449 * the VM after successfully re-checking the accessibility state. Note
8450 * that in case of normal Machine or SnapshotMachine uninitialization (as
8451 * a result of unregistering or deleting the snapshot), outdated media
8452 * attachments will already be uninitialized and deleted, so this
8453 * code will not affect them. */
8454 if ( !!mMediaData
8455 && (!isSessionMachine())
8456 )
8457 {
8458 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8459 it != mMediaData->mAttachments.end();
8460 ++it)
8461 {
8462 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8463 if (pMedium.isNull())
8464 continue;
8465 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8466 AssertComRC(rc);
8467 }
8468 }
8469
8470 if (!isSessionMachine() && !isSnapshotMachine())
8471 {
8472 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8473 if (mData->mFirstSnapshot)
8474 {
8475 // snapshots tree is protected by machine write lock; strictly
8476 // this isn't necessary here since we're deleting the entire
8477 // machine, but otherwise we assert in Snapshot::uninit()
8478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8479 mData->mFirstSnapshot->uninit();
8480 mData->mFirstSnapshot.setNull();
8481 }
8482
8483 mData->mCurrentSnapshot.setNull();
8484 }
8485
8486 /* free data structures (the essential mData structure is not freed here
8487 * since it may be still in use) */
8488 mMediaData.free();
8489 mStorageControllers.free();
8490 mHWData.free();
8491 mUserData.free();
8492 mSSData.free();
8493}
8494
8495/**
8496 * Returns a pointer to the Machine object for this machine that acts like a
8497 * parent for complex machine data objects such as shared folders, etc.
8498 *
8499 * For primary Machine objects and for SnapshotMachine objects, returns this
8500 * object's pointer itself. For SessionMachine objects, returns the peer
8501 * (primary) machine pointer.
8502 */
8503Machine* Machine::getMachine()
8504{
8505 if (isSessionMachine())
8506 return (Machine*)mPeer;
8507 return this;
8508}
8509
8510/**
8511 * Makes sure that there are no machine state dependents. If necessary, waits
8512 * for the number of dependents to drop to zero.
8513 *
8514 * Make sure this method is called from under this object's write lock to
8515 * guarantee that no new dependents may be added when this method returns
8516 * control to the caller.
8517 *
8518 * @note Locks this object for writing. The lock will be released while waiting
8519 * (if necessary).
8520 *
8521 * @warning To be used only in methods that change the machine state!
8522 */
8523void Machine::ensureNoStateDependencies()
8524{
8525 AssertReturnVoid(isWriteLockOnCurrentThread());
8526
8527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8528
8529 /* Wait for all state dependents if necessary */
8530 if (mData->mMachineStateDeps != 0)
8531 {
8532 /* lazy semaphore creation */
8533 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8534 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8535
8536 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8537 mData->mMachineStateDeps));
8538
8539 ++mData->mMachineStateChangePending;
8540
8541 /* reset the semaphore before waiting, the last dependent will signal
8542 * it */
8543 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8544
8545 alock.release();
8546
8547 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8548
8549 alock.acquire();
8550
8551 -- mData->mMachineStateChangePending;
8552 }
8553}
8554
8555/**
8556 * Changes the machine state and informs callbacks.
8557 *
8558 * This method is not intended to fail so it either returns S_OK or asserts (and
8559 * returns a failure).
8560 *
8561 * @note Locks this object for writing.
8562 */
8563HRESULT Machine::setMachineState(MachineState_T aMachineState)
8564{
8565 LogFlowThisFuncEnter();
8566 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8567
8568 AutoCaller autoCaller(this);
8569 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8570
8571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8572
8573 /* wait for state dependents to drop to zero */
8574 ensureNoStateDependencies();
8575
8576 if (mData->mMachineState != aMachineState)
8577 {
8578 mData->mMachineState = aMachineState;
8579
8580 RTTimeNow(&mData->mLastStateChange);
8581
8582 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8583 }
8584
8585 LogFlowThisFuncLeave();
8586 return S_OK;
8587}
8588
8589/**
8590 * Searches for a shared folder with the given logical name
8591 * in the collection of shared folders.
8592 *
8593 * @param aName logical name of the shared folder
8594 * @param aSharedFolder where to return the found object
8595 * @param aSetError whether to set the error info if the folder is
8596 * not found
8597 * @return
8598 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8599 *
8600 * @note
8601 * must be called from under the object's lock!
8602 */
8603HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8604 ComObjPtr<SharedFolder> &aSharedFolder,
8605 bool aSetError /* = false */)
8606{
8607 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8608 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8609 it != mHWData->mSharedFolders.end();
8610 ++it)
8611 {
8612 SharedFolder *pSF = *it;
8613 AutoCaller autoCaller(pSF);
8614 if (pSF->getName() == aName)
8615 {
8616 aSharedFolder = pSF;
8617 rc = S_OK;
8618 break;
8619 }
8620 }
8621
8622 if (aSetError && FAILED(rc))
8623 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8624
8625 return rc;
8626}
8627
8628/**
8629 * Initializes all machine instance data from the given settings structures
8630 * from XML. The exception is the machine UUID which needs special handling
8631 * depending on the caller's use case, so the caller needs to set that herself.
8632 *
8633 * This gets called in several contexts during machine initialization:
8634 *
8635 * -- When machine XML exists on disk already and needs to be loaded into memory,
8636 * for example, from registeredInit() to load all registered machines on
8637 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8638 * attached to the machine should be part of some media registry already.
8639 *
8640 * -- During OVF import, when a machine config has been constructed from an
8641 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8642 * ensure that the media listed as attachments in the config (which have
8643 * been imported from the OVF) receive the correct registry ID.
8644 *
8645 * -- During VM cloning.
8646 *
8647 * @param config Machine settings from XML.
8648 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8649 * @return
8650 */
8651HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8652 const Guid *puuidRegistry)
8653{
8654 // copy name, description, OS type, teleporter, UTC etc.
8655 #define DECODE_STR_MAX _1M
8656 mUserData->s = config.machineUserData;
8657
8658 // Decode the Icon overide data from config userdata and set onto Machine.
8659 const char* pszStr = config.machineUserData.ovIcon.c_str();
8660 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8661 if (cbOut > DECODE_STR_MAX)
8662 return setError(E_FAIL,
8663 tr("Icon Data too long.'%d' > '%d'"),
8664 cbOut,
8665 DECODE_STR_MAX);
8666 com::SafeArray<BYTE> iconByte(cbOut);
8667 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8668 if (FAILED(rc))
8669 return setError(E_FAIL,
8670 tr("Failure to Decode Icon Data. '%s' (%d)"),
8671 pszStr,
8672 rc);
8673 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8674
8675 // look up the object by Id to check it is valid
8676 ComPtr<IGuestOSType> guestOSType;
8677 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8678 guestOSType.asOutParam());
8679 if (FAILED(rc)) return rc;
8680
8681 // stateFile (optional)
8682 if (config.strStateFile.isEmpty())
8683 mSSData->strStateFilePath.setNull();
8684 else
8685 {
8686 Utf8Str stateFilePathFull(config.strStateFile);
8687 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8688 if (RT_FAILURE(vrc))
8689 return setError(E_FAIL,
8690 tr("Invalid saved state file path '%s' (%Rrc)"),
8691 config.strStateFile.c_str(),
8692 vrc);
8693 mSSData->strStateFilePath = stateFilePathFull;
8694 }
8695
8696 // snapshot folder needs special processing so set it again
8697 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8698 if (FAILED(rc)) return rc;
8699
8700 /* Copy the extra data items (Not in any case config is already the same as
8701 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8702 * make sure the extra data map is copied). */
8703 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8704
8705 /* currentStateModified (optional, default is true) */
8706 mData->mCurrentStateModified = config.fCurrentStateModified;
8707
8708 mData->mLastStateChange = config.timeLastStateChange;
8709
8710 /*
8711 * note: all mUserData members must be assigned prior this point because
8712 * we need to commit changes in order to let mUserData be shared by all
8713 * snapshot machine instances.
8714 */
8715 mUserData.commitCopy();
8716
8717 // machine registry, if present (must be loaded before snapshots)
8718 if (config.canHaveOwnMediaRegistry())
8719 {
8720 // determine machine folder
8721 Utf8Str strMachineFolder = getSettingsFileFull();
8722 strMachineFolder.stripFilename();
8723 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8724 config.mediaRegistry,
8725 strMachineFolder);
8726 if (FAILED(rc)) return rc;
8727 }
8728
8729 /* Snapshot node (optional) */
8730 size_t cRootSnapshots;
8731 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8732 {
8733 // there must be only one root snapshot
8734 Assert(cRootSnapshots == 1);
8735
8736 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8737
8738 rc = loadSnapshot(snap,
8739 config.uuidCurrentSnapshot,
8740 NULL); // no parent == first snapshot
8741 if (FAILED(rc)) return rc;
8742 }
8743
8744 // hardware data
8745 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8746 if (FAILED(rc)) return rc;
8747
8748 // load storage controllers
8749 rc = loadStorageControllers(config.storageMachine,
8750 puuidRegistry,
8751 NULL /* puuidSnapshot */);
8752 if (FAILED(rc)) return rc;
8753
8754 /*
8755 * NOTE: the assignment below must be the last thing to do,
8756 * otherwise it will be not possible to change the settings
8757 * somewhere in the code above because all setters will be
8758 * blocked by checkStateDependency(MutableStateDep).
8759 */
8760
8761 /* set the machine state to Aborted or Saved when appropriate */
8762 if (config.fAborted)
8763 {
8764 mSSData->strStateFilePath.setNull();
8765
8766 /* no need to use setMachineState() during init() */
8767 mData->mMachineState = MachineState_Aborted;
8768 }
8769 else if (!mSSData->strStateFilePath.isEmpty())
8770 {
8771 /* no need to use setMachineState() during init() */
8772 mData->mMachineState = MachineState_Saved;
8773 }
8774
8775 // after loading settings, we are no longer different from the XML on disk
8776 mData->flModifications = 0;
8777
8778 return S_OK;
8779}
8780
8781/**
8782 * Recursively loads all snapshots starting from the given.
8783 *
8784 * @param aNode <Snapshot> node.
8785 * @param aCurSnapshotId Current snapshot ID from the settings file.
8786 * @param aParentSnapshot Parent snapshot.
8787 */
8788HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8789 const Guid &aCurSnapshotId,
8790 Snapshot *aParentSnapshot)
8791{
8792 AssertReturn(!isSnapshotMachine(), E_FAIL);
8793 AssertReturn(!isSessionMachine(), E_FAIL);
8794
8795 HRESULT rc = S_OK;
8796
8797 Utf8Str strStateFile;
8798 if (!data.strStateFile.isEmpty())
8799 {
8800 /* optional */
8801 strStateFile = data.strStateFile;
8802 int vrc = calculateFullPath(strStateFile, strStateFile);
8803 if (RT_FAILURE(vrc))
8804 return setError(E_FAIL,
8805 tr("Invalid saved state file path '%s' (%Rrc)"),
8806 strStateFile.c_str(),
8807 vrc);
8808 }
8809
8810 /* create a snapshot machine object */
8811 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8812 pSnapshotMachine.createObject();
8813 rc = pSnapshotMachine->initFromSettings(this,
8814 data.hardware,
8815 &data.debugging,
8816 &data.autostart,
8817 data.storage,
8818 data.uuid.ref(),
8819 strStateFile);
8820 if (FAILED(rc)) return rc;
8821
8822 /* create a snapshot object */
8823 ComObjPtr<Snapshot> pSnapshot;
8824 pSnapshot.createObject();
8825 /* initialize the snapshot */
8826 rc = pSnapshot->init(mParent, // VirtualBox object
8827 data.uuid,
8828 data.strName,
8829 data.strDescription,
8830 data.timestamp,
8831 pSnapshotMachine,
8832 aParentSnapshot);
8833 if (FAILED(rc)) return rc;
8834
8835 /* memorize the first snapshot if necessary */
8836 if (!mData->mFirstSnapshot)
8837 mData->mFirstSnapshot = pSnapshot;
8838
8839 /* memorize the current snapshot when appropriate */
8840 if ( !mData->mCurrentSnapshot
8841 && pSnapshot->getId() == aCurSnapshotId
8842 )
8843 mData->mCurrentSnapshot = pSnapshot;
8844
8845 // now create the children
8846 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8847 it != data.llChildSnapshots.end();
8848 ++it)
8849 {
8850 const settings::Snapshot &childData = *it;
8851 // recurse
8852 rc = loadSnapshot(childData,
8853 aCurSnapshotId,
8854 pSnapshot); // parent = the one we created above
8855 if (FAILED(rc)) return rc;
8856 }
8857
8858 return rc;
8859}
8860
8861/**
8862 * Loads settings into mHWData.
8863 *
8864 * @param data Reference to the hardware settings.
8865 * @param pDbg Pointer to the debugging settings.
8866 * @param pAutostart Pointer to the autostart settings.
8867 */
8868HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8869 const settings::Autostart *pAutostart)
8870{
8871 AssertReturn(!isSessionMachine(), E_FAIL);
8872
8873 HRESULT rc = S_OK;
8874
8875 try
8876 {
8877 /* The hardware version attribute (optional). */
8878 mHWData->mHWVersion = data.strVersion;
8879 mHWData->mHardwareUUID = data.uuid;
8880
8881 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8882 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8883 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8884 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8885 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8886 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8887 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8888 mHWData->mPAEEnabled = data.fPAE;
8889 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8890 mHWData->mLongMode = data.enmLongMode;
8891 mHWData->mCPUCount = data.cCPUs;
8892 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8893 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8894
8895 // cpu
8896 if (mHWData->mCPUHotPlugEnabled)
8897 {
8898 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8899 it != data.llCpus.end();
8900 ++it)
8901 {
8902 const settings::Cpu &cpu = *it;
8903
8904 mHWData->mCPUAttached[cpu.ulId] = true;
8905 }
8906 }
8907
8908 // cpuid leafs
8909 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8910 it != data.llCpuIdLeafs.end();
8911 ++it)
8912 {
8913 const settings::CpuIdLeaf &leaf = *it;
8914
8915 switch (leaf.ulId)
8916 {
8917 case 0x0:
8918 case 0x1:
8919 case 0x2:
8920 case 0x3:
8921 case 0x4:
8922 case 0x5:
8923 case 0x6:
8924 case 0x7:
8925 case 0x8:
8926 case 0x9:
8927 case 0xA:
8928 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8929 break;
8930
8931 case 0x80000000:
8932 case 0x80000001:
8933 case 0x80000002:
8934 case 0x80000003:
8935 case 0x80000004:
8936 case 0x80000005:
8937 case 0x80000006:
8938 case 0x80000007:
8939 case 0x80000008:
8940 case 0x80000009:
8941 case 0x8000000A:
8942 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8943 break;
8944
8945 default:
8946 /* just ignore */
8947 break;
8948 }
8949 }
8950
8951 mHWData->mMemorySize = data.ulMemorySizeMB;
8952 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8953
8954 // boot order
8955 for (size_t i = 0;
8956 i < RT_ELEMENTS(mHWData->mBootOrder);
8957 i++)
8958 {
8959 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8960 if (it == data.mapBootOrder.end())
8961 mHWData->mBootOrder[i] = DeviceType_Null;
8962 else
8963 mHWData->mBootOrder[i] = it->second;
8964 }
8965
8966 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8967 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8968 mHWData->mMonitorCount = data.cMonitors;
8969 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8970 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8971 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8972 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8973 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8974 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
8975 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8976 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8977 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8978 mHWData->mVideoCaptureFps = data.ulVideoCaptureFps;
8979 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8980 mHWData->mFirmwareType = data.firmwareType;
8981 mHWData->mPointingHIDType = data.pointingHIDType;
8982 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8983 mHWData->mChipsetType = data.chipsetType;
8984 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8985 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8986 mHWData->mHPETEnabled = data.fHPETEnabled;
8987
8988 /* VRDEServer */
8989 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8990 if (FAILED(rc)) return rc;
8991
8992 /* BIOS */
8993 rc = mBIOSSettings->loadSettings(data.biosSettings);
8994 if (FAILED(rc)) return rc;
8995
8996 // Bandwidth control (must come before network adapters)
8997 rc = mBandwidthControl->loadSettings(data.ioSettings);
8998 if (FAILED(rc)) return rc;
8999
9000 /* USB Controller */
9001 rc = mUSBController->loadSettings(data.usbController);
9002 if (FAILED(rc)) return rc;
9003
9004 // network adapters
9005 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9006 uint32_t oldCount = mNetworkAdapters.size();
9007 if (newCount > oldCount)
9008 {
9009 mNetworkAdapters.resize(newCount);
9010 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9011 {
9012 unconst(mNetworkAdapters[slot]).createObject();
9013 mNetworkAdapters[slot]->init(this, slot);
9014 }
9015 }
9016 else if (newCount < oldCount)
9017 mNetworkAdapters.resize(newCount);
9018 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9019 it != data.llNetworkAdapters.end();
9020 ++it)
9021 {
9022 const settings::NetworkAdapter &nic = *it;
9023
9024 /* slot unicity is guaranteed by XML Schema */
9025 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9026 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9027 if (FAILED(rc)) return rc;
9028 }
9029
9030 // serial ports
9031 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9032 it != data.llSerialPorts.end();
9033 ++it)
9034 {
9035 const settings::SerialPort &s = *it;
9036
9037 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9038 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9039 if (FAILED(rc)) return rc;
9040 }
9041
9042 // parallel ports (optional)
9043 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9044 it != data.llParallelPorts.end();
9045 ++it)
9046 {
9047 const settings::ParallelPort &p = *it;
9048
9049 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9050 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9051 if (FAILED(rc)) return rc;
9052 }
9053
9054 /* AudioAdapter */
9055 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9056 if (FAILED(rc)) return rc;
9057
9058 /* Shared folders */
9059 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9060 it != data.llSharedFolders.end();
9061 ++it)
9062 {
9063 const settings::SharedFolder &sf = *it;
9064
9065 ComObjPtr<SharedFolder> sharedFolder;
9066 /* Check for double entries. Not allowed! */
9067 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9068 if (SUCCEEDED(rc))
9069 return setError(VBOX_E_OBJECT_IN_USE,
9070 tr("Shared folder named '%s' already exists"),
9071 sf.strName.c_str());
9072
9073 /* Create the new shared folder. Don't break on error. This will be
9074 * reported when the machine starts. */
9075 sharedFolder.createObject();
9076 rc = sharedFolder->init(getMachine(),
9077 sf.strName,
9078 sf.strHostPath,
9079 RT_BOOL(sf.fWritable),
9080 RT_BOOL(sf.fAutoMount),
9081 false /* fFailOnError */);
9082 if (FAILED(rc)) return rc;
9083 mHWData->mSharedFolders.push_back(sharedFolder);
9084 }
9085
9086 // Clipboard
9087 mHWData->mClipboardMode = data.clipboardMode;
9088
9089 // drag'n'drop
9090 mHWData->mDragAndDropMode = data.dragAndDropMode;
9091
9092 // guest settings
9093 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9094
9095 // IO settings
9096 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9097 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9098
9099 // Host PCI devices
9100 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9101 it != data.pciAttachments.end();
9102 ++it)
9103 {
9104 const settings::HostPCIDeviceAttachment &hpda = *it;
9105 ComObjPtr<PCIDeviceAttachment> pda;
9106
9107 pda.createObject();
9108 pda->loadSettings(this, hpda);
9109 mHWData->mPCIDeviceAssignments.push_back(pda);
9110 }
9111
9112 /*
9113 * (The following isn't really real hardware, but it lives in HWData
9114 * for reasons of convenience.)
9115 */
9116
9117#ifdef VBOX_WITH_GUEST_PROPS
9118 /* Guest properties (optional) */
9119 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9120 it != data.llGuestProperties.end();
9121 ++it)
9122 {
9123 const settings::GuestProperty &prop = *it;
9124 uint32_t fFlags = guestProp::NILFLAG;
9125 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9126 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9127 mHWData->mGuestProperties[prop.strName] = property;
9128 }
9129
9130 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9131#endif /* VBOX_WITH_GUEST_PROPS defined */
9132
9133 rc = loadDebugging(pDbg);
9134 if (FAILED(rc))
9135 return rc;
9136
9137 mHWData->mAutostart = *pAutostart;
9138
9139 /* default frontend */
9140 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9141 }
9142 catch(std::bad_alloc &)
9143 {
9144 return E_OUTOFMEMORY;
9145 }
9146
9147 AssertComRC(rc);
9148 return rc;
9149}
9150
9151/**
9152 * Called from Machine::loadHardware() to load the debugging settings of the
9153 * machine.
9154 *
9155 * @param pDbg Pointer to the settings.
9156 */
9157HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9158{
9159 mHWData->mDebugging = *pDbg;
9160 /* no more processing currently required, this will probably change. */
9161 return S_OK;
9162}
9163
9164/**
9165 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9166 *
9167 * @param data
9168 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9169 * @param puuidSnapshot
9170 * @return
9171 */
9172HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9173 const Guid *puuidRegistry,
9174 const Guid *puuidSnapshot)
9175{
9176 AssertReturn(!isSessionMachine(), E_FAIL);
9177
9178 HRESULT rc = S_OK;
9179
9180 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9181 it != data.llStorageControllers.end();
9182 ++it)
9183 {
9184 const settings::StorageController &ctlData = *it;
9185
9186 ComObjPtr<StorageController> pCtl;
9187 /* Try to find one with the name first. */
9188 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9189 if (SUCCEEDED(rc))
9190 return setError(VBOX_E_OBJECT_IN_USE,
9191 tr("Storage controller named '%s' already exists"),
9192 ctlData.strName.c_str());
9193
9194 pCtl.createObject();
9195 rc = pCtl->init(this,
9196 ctlData.strName,
9197 ctlData.storageBus,
9198 ctlData.ulInstance,
9199 ctlData.fBootable);
9200 if (FAILED(rc)) return rc;
9201
9202 mStorageControllers->push_back(pCtl);
9203
9204 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9205 if (FAILED(rc)) return rc;
9206
9207 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9208 if (FAILED(rc)) return rc;
9209
9210 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9211 if (FAILED(rc)) return rc;
9212
9213 /* Set IDE emulation settings (only for AHCI controller). */
9214 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9215 {
9216 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9217 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9218 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9219 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9220 )
9221 return rc;
9222 }
9223
9224 /* Load the attached devices now. */
9225 rc = loadStorageDevices(pCtl,
9226 ctlData,
9227 puuidRegistry,
9228 puuidSnapshot);
9229 if (FAILED(rc)) return rc;
9230 }
9231
9232 return S_OK;
9233}
9234
9235/**
9236 * Called from loadStorageControllers for a controller's devices.
9237 *
9238 * @param aStorageController
9239 * @param data
9240 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9241 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9242 * @return
9243 */
9244HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9245 const settings::StorageController &data,
9246 const Guid *puuidRegistry,
9247 const Guid *puuidSnapshot)
9248{
9249 HRESULT rc = S_OK;
9250
9251 /* paranoia: detect duplicate attachments */
9252 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9253 it != data.llAttachedDevices.end();
9254 ++it)
9255 {
9256 const settings::AttachedDevice &ad = *it;
9257
9258 for (settings::AttachedDevicesList::const_iterator it2 = it;
9259 it2 != data.llAttachedDevices.end();
9260 ++it2)
9261 {
9262 if (it == it2)
9263 continue;
9264
9265 const settings::AttachedDevice &ad2 = *it2;
9266
9267 if ( ad.lPort == ad2.lPort
9268 && ad.lDevice == ad2.lDevice)
9269 {
9270 return setError(E_FAIL,
9271 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9272 aStorageController->getName().c_str(),
9273 ad.lPort,
9274 ad.lDevice,
9275 mUserData->s.strName.c_str());
9276 }
9277 }
9278 }
9279
9280 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9281 it != data.llAttachedDevices.end();
9282 ++it)
9283 {
9284 const settings::AttachedDevice &dev = *it;
9285 ComObjPtr<Medium> medium;
9286
9287 switch (dev.deviceType)
9288 {
9289 case DeviceType_Floppy:
9290 case DeviceType_DVD:
9291 if (dev.strHostDriveSrc.isNotEmpty())
9292 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9293 else
9294 rc = mParent->findRemoveableMedium(dev.deviceType,
9295 dev.uuid,
9296 false /* fRefresh */,
9297 false /* aSetError */,
9298 medium);
9299 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9300 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9301 rc = S_OK;
9302 break;
9303
9304 case DeviceType_HardDisk:
9305 {
9306 /* find a hard disk by UUID */
9307 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9308 if (FAILED(rc))
9309 {
9310 if (isSnapshotMachine())
9311 {
9312 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9313 // so the user knows that the bad disk is in a snapshot somewhere
9314 com::ErrorInfo info;
9315 return setError(E_FAIL,
9316 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9317 puuidSnapshot->raw(),
9318 info.getText().raw());
9319 }
9320 else
9321 return rc;
9322 }
9323
9324 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9325
9326 if (medium->getType() == MediumType_Immutable)
9327 {
9328 if (isSnapshotMachine())
9329 return setError(E_FAIL,
9330 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9331 "of the virtual machine '%s' ('%s')"),
9332 medium->getLocationFull().c_str(),
9333 dev.uuid.raw(),
9334 puuidSnapshot->raw(),
9335 mUserData->s.strName.c_str(),
9336 mData->m_strConfigFileFull.c_str());
9337
9338 return setError(E_FAIL,
9339 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9340 medium->getLocationFull().c_str(),
9341 dev.uuid.raw(),
9342 mUserData->s.strName.c_str(),
9343 mData->m_strConfigFileFull.c_str());
9344 }
9345
9346 if (medium->getType() == MediumType_MultiAttach)
9347 {
9348 if (isSnapshotMachine())
9349 return setError(E_FAIL,
9350 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9351 "of the virtual machine '%s' ('%s')"),
9352 medium->getLocationFull().c_str(),
9353 dev.uuid.raw(),
9354 puuidSnapshot->raw(),
9355 mUserData->s.strName.c_str(),
9356 mData->m_strConfigFileFull.c_str());
9357
9358 return setError(E_FAIL,
9359 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9360 medium->getLocationFull().c_str(),
9361 dev.uuid.raw(),
9362 mUserData->s.strName.c_str(),
9363 mData->m_strConfigFileFull.c_str());
9364 }
9365
9366 if ( !isSnapshotMachine()
9367 && medium->getChildren().size() != 0
9368 )
9369 return setError(E_FAIL,
9370 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9371 "because it has %d differencing child hard disks"),
9372 medium->getLocationFull().c_str(),
9373 dev.uuid.raw(),
9374 mUserData->s.strName.c_str(),
9375 mData->m_strConfigFileFull.c_str(),
9376 medium->getChildren().size());
9377
9378 if (findAttachment(mMediaData->mAttachments,
9379 medium))
9380 return setError(E_FAIL,
9381 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9382 medium->getLocationFull().c_str(),
9383 dev.uuid.raw(),
9384 mUserData->s.strName.c_str(),
9385 mData->m_strConfigFileFull.c_str());
9386
9387 break;
9388 }
9389
9390 default:
9391 return setError(E_FAIL,
9392 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9393 medium->getLocationFull().c_str(),
9394 mUserData->s.strName.c_str(),
9395 mData->m_strConfigFileFull.c_str());
9396 }
9397
9398 if (FAILED(rc))
9399 break;
9400
9401 /* Bandwidth groups are loaded at this point. */
9402 ComObjPtr<BandwidthGroup> pBwGroup;
9403
9404 if (!dev.strBwGroup.isEmpty())
9405 {
9406 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9407 if (FAILED(rc))
9408 return setError(E_FAIL,
9409 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9410 medium->getLocationFull().c_str(),
9411 dev.strBwGroup.c_str(),
9412 mUserData->s.strName.c_str(),
9413 mData->m_strConfigFileFull.c_str());
9414 pBwGroup->reference();
9415 }
9416
9417 const Bstr controllerName = aStorageController->getName();
9418 ComObjPtr<MediumAttachment> pAttachment;
9419 pAttachment.createObject();
9420 rc = pAttachment->init(this,
9421 medium,
9422 controllerName,
9423 dev.lPort,
9424 dev.lDevice,
9425 dev.deviceType,
9426 false,
9427 dev.fPassThrough,
9428 dev.fTempEject,
9429 dev.fNonRotational,
9430 dev.fDiscard,
9431 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9432 if (FAILED(rc)) break;
9433
9434 /* associate the medium with this machine and snapshot */
9435 if (!medium.isNull())
9436 {
9437 AutoCaller medCaller(medium);
9438 if (FAILED(medCaller.rc())) return medCaller.rc();
9439 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9440
9441 if (isSnapshotMachine())
9442 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9443 else
9444 rc = medium->addBackReference(mData->mUuid);
9445 /* If the medium->addBackReference fails it sets an appropriate
9446 * error message, so no need to do any guesswork here. */
9447
9448 if (puuidRegistry)
9449 // caller wants registry ID to be set on all attached media (OVF import case)
9450 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9451 }
9452
9453 if (FAILED(rc))
9454 break;
9455
9456 /* back up mMediaData to let registeredInit() properly rollback on failure
9457 * (= limited accessibility) */
9458 setModified(IsModified_Storage);
9459 mMediaData.backup();
9460 mMediaData->mAttachments.push_back(pAttachment);
9461 }
9462
9463 return rc;
9464}
9465
9466/**
9467 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9468 *
9469 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9470 * @param aSnapshot where to return the found snapshot
9471 * @param aSetError true to set extended error info on failure
9472 */
9473HRESULT Machine::findSnapshotById(const Guid &aId,
9474 ComObjPtr<Snapshot> &aSnapshot,
9475 bool aSetError /* = false */)
9476{
9477 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9478
9479 if (!mData->mFirstSnapshot)
9480 {
9481 if (aSetError)
9482 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9483 return E_FAIL;
9484 }
9485
9486 if (aId.isZero())
9487 aSnapshot = mData->mFirstSnapshot;
9488 else
9489 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9490
9491 if (!aSnapshot)
9492 {
9493 if (aSetError)
9494 return setError(E_FAIL,
9495 tr("Could not find a snapshot with UUID {%s}"),
9496 aId.toString().c_str());
9497 return E_FAIL;
9498 }
9499
9500 return S_OK;
9501}
9502
9503/**
9504 * Returns the snapshot with the given name or fails of no such snapshot.
9505 *
9506 * @param aName snapshot name to find
9507 * @param aSnapshot where to return the found snapshot
9508 * @param aSetError true to set extended error info on failure
9509 */
9510HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9511 ComObjPtr<Snapshot> &aSnapshot,
9512 bool aSetError /* = false */)
9513{
9514 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9515
9516 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9517
9518 if (!mData->mFirstSnapshot)
9519 {
9520 if (aSetError)
9521 return setError(VBOX_E_OBJECT_NOT_FOUND,
9522 tr("This machine does not have any snapshots"));
9523 return VBOX_E_OBJECT_NOT_FOUND;
9524 }
9525
9526 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9527
9528 if (!aSnapshot)
9529 {
9530 if (aSetError)
9531 return setError(VBOX_E_OBJECT_NOT_FOUND,
9532 tr("Could not find a snapshot named '%s'"), strName.c_str());
9533 return VBOX_E_OBJECT_NOT_FOUND;
9534 }
9535
9536 return S_OK;
9537}
9538
9539/**
9540 * Returns a storage controller object with the given name.
9541 *
9542 * @param aName storage controller name to find
9543 * @param aStorageController where to return the found storage controller
9544 * @param aSetError true to set extended error info on failure
9545 */
9546HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9547 ComObjPtr<StorageController> &aStorageController,
9548 bool aSetError /* = false */)
9549{
9550 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9551
9552 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9553 it != mStorageControllers->end();
9554 ++it)
9555 {
9556 if ((*it)->getName() == aName)
9557 {
9558 aStorageController = (*it);
9559 return S_OK;
9560 }
9561 }
9562
9563 if (aSetError)
9564 return setError(VBOX_E_OBJECT_NOT_FOUND,
9565 tr("Could not find a storage controller named '%s'"),
9566 aName.c_str());
9567 return VBOX_E_OBJECT_NOT_FOUND;
9568}
9569
9570HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9571 MediaData::AttachmentList &atts)
9572{
9573 AutoCaller autoCaller(this);
9574 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9575
9576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9577
9578 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9579 it != mMediaData->mAttachments.end();
9580 ++it)
9581 {
9582 const ComObjPtr<MediumAttachment> &pAtt = *it;
9583
9584 // should never happen, but deal with NULL pointers in the list.
9585 AssertStmt(!pAtt.isNull(), continue);
9586
9587 // getControllerName() needs caller+read lock
9588 AutoCaller autoAttCaller(pAtt);
9589 if (FAILED(autoAttCaller.rc()))
9590 {
9591 atts.clear();
9592 return autoAttCaller.rc();
9593 }
9594 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9595
9596 if (pAtt->getControllerName() == aName)
9597 atts.push_back(pAtt);
9598 }
9599
9600 return S_OK;
9601}
9602
9603/**
9604 * Helper for #saveSettings. Cares about renaming the settings directory and
9605 * file if the machine name was changed and about creating a new settings file
9606 * if this is a new machine.
9607 *
9608 * @note Must be never called directly but only from #saveSettings().
9609 */
9610HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9611{
9612 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9613
9614 HRESULT rc = S_OK;
9615
9616 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9617
9618 /// @todo need to handle primary group change, too
9619
9620 /* attempt to rename the settings file if machine name is changed */
9621 if ( mUserData->s.fNameSync
9622 && mUserData.isBackedUp()
9623 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9624 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9625 )
9626 {
9627 bool dirRenamed = false;
9628 bool fileRenamed = false;
9629
9630 Utf8Str configFile, newConfigFile;
9631 Utf8Str configFilePrev, newConfigFilePrev;
9632 Utf8Str configDir, newConfigDir;
9633
9634 do
9635 {
9636 int vrc = VINF_SUCCESS;
9637
9638 Utf8Str name = mUserData.backedUpData()->s.strName;
9639 Utf8Str newName = mUserData->s.strName;
9640 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9641 if (group == "/")
9642 group.setNull();
9643 Utf8Str newGroup = mUserData->s.llGroups.front();
9644 if (newGroup == "/")
9645 newGroup.setNull();
9646
9647 configFile = mData->m_strConfigFileFull;
9648
9649 /* first, rename the directory if it matches the group and machine name */
9650 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9651 group.c_str(), RTPATH_DELIMITER, name.c_str());
9652 /** @todo hack, make somehow use of ComposeMachineFilename */
9653 if (mUserData->s.fDirectoryIncludesUUID)
9654 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9655 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9656 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9657 /** @todo hack, make somehow use of ComposeMachineFilename */
9658 if (mUserData->s.fDirectoryIncludesUUID)
9659 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9660 configDir = configFile;
9661 configDir.stripFilename();
9662 newConfigDir = configDir;
9663 if ( configDir.length() >= groupPlusName.length()
9664 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9665 {
9666 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9667 Utf8Str newConfigBaseDir(newConfigDir);
9668 newConfigDir.append(newGroupPlusName);
9669 /* consistency: use \ if appropriate on the platform */
9670 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9671 /* new dir and old dir cannot be equal here because of 'if'
9672 * above and because name != newName */
9673 Assert(configDir != newConfigDir);
9674 if (!fSettingsFileIsNew)
9675 {
9676 /* perform real rename only if the machine is not new */
9677 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9678 if ( vrc == VERR_FILE_NOT_FOUND
9679 || vrc == VERR_PATH_NOT_FOUND)
9680 {
9681 /* create the parent directory, then retry renaming */
9682 Utf8Str parent(newConfigDir);
9683 parent.stripFilename();
9684 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9685 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9686 }
9687 if (RT_FAILURE(vrc))
9688 {
9689 rc = setError(E_FAIL,
9690 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9691 configDir.c_str(),
9692 newConfigDir.c_str(),
9693 vrc);
9694 break;
9695 }
9696 /* delete subdirectories which are no longer needed */
9697 Utf8Str dir(configDir);
9698 dir.stripFilename();
9699 while (dir != newConfigBaseDir && dir != ".")
9700 {
9701 vrc = RTDirRemove(dir.c_str());
9702 if (RT_FAILURE(vrc))
9703 break;
9704 dir.stripFilename();
9705 }
9706 dirRenamed = true;
9707 }
9708 }
9709
9710 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9711 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9712
9713 /* then try to rename the settings file itself */
9714 if (newConfigFile != configFile)
9715 {
9716 /* get the path to old settings file in renamed directory */
9717 configFile = Utf8StrFmt("%s%c%s",
9718 newConfigDir.c_str(),
9719 RTPATH_DELIMITER,
9720 RTPathFilename(configFile.c_str()));
9721 if (!fSettingsFileIsNew)
9722 {
9723 /* perform real rename only if the machine is not new */
9724 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9725 if (RT_FAILURE(vrc))
9726 {
9727 rc = setError(E_FAIL,
9728 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9729 configFile.c_str(),
9730 newConfigFile.c_str(),
9731 vrc);
9732 break;
9733 }
9734 fileRenamed = true;
9735 configFilePrev = configFile;
9736 configFilePrev += "-prev";
9737 newConfigFilePrev = newConfigFile;
9738 newConfigFilePrev += "-prev";
9739 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9740 }
9741 }
9742
9743 // update m_strConfigFileFull amd mConfigFile
9744 mData->m_strConfigFileFull = newConfigFile;
9745 // compute the relative path too
9746 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9747
9748 // store the old and new so that VirtualBox::saveSettings() can update
9749 // the media registry
9750 if ( mData->mRegistered
9751 && configDir != newConfigDir)
9752 {
9753 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9754
9755 if (pfNeedsGlobalSaveSettings)
9756 *pfNeedsGlobalSaveSettings = true;
9757 }
9758
9759 // in the saved state file path, replace the old directory with the new directory
9760 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9761 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9762
9763 // and do the same thing for the saved state file paths of all the online snapshots
9764 if (mData->mFirstSnapshot)
9765 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9766 newConfigDir.c_str());
9767 }
9768 while (0);
9769
9770 if (FAILED(rc))
9771 {
9772 /* silently try to rename everything back */
9773 if (fileRenamed)
9774 {
9775 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9776 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9777 }
9778 if (dirRenamed)
9779 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9780 }
9781
9782 if (FAILED(rc)) return rc;
9783 }
9784
9785 if (fSettingsFileIsNew)
9786 {
9787 /* create a virgin config file */
9788 int vrc = VINF_SUCCESS;
9789
9790 /* ensure the settings directory exists */
9791 Utf8Str path(mData->m_strConfigFileFull);
9792 path.stripFilename();
9793 if (!RTDirExists(path.c_str()))
9794 {
9795 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9796 if (RT_FAILURE(vrc))
9797 {
9798 return setError(E_FAIL,
9799 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9800 path.c_str(),
9801 vrc);
9802 }
9803 }
9804
9805 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9806 path = Utf8Str(mData->m_strConfigFileFull);
9807 RTFILE f = NIL_RTFILE;
9808 vrc = RTFileOpen(&f, path.c_str(),
9809 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9810 if (RT_FAILURE(vrc))
9811 return setError(E_FAIL,
9812 tr("Could not create the settings file '%s' (%Rrc)"),
9813 path.c_str(),
9814 vrc);
9815 RTFileClose(f);
9816 }
9817
9818 return rc;
9819}
9820
9821/**
9822 * Saves and commits machine data, user data and hardware data.
9823 *
9824 * Note that on failure, the data remains uncommitted.
9825 *
9826 * @a aFlags may combine the following flags:
9827 *
9828 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9829 * Used when saving settings after an operation that makes them 100%
9830 * correspond to the settings from the current snapshot.
9831 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9832 * #isReallyModified() returns false. This is necessary for cases when we
9833 * change machine data directly, not through the backup()/commit() mechanism.
9834 * - SaveS_Force: settings will be saved without doing a deep compare of the
9835 * settings structures. This is used when this is called because snapshots
9836 * have changed to avoid the overhead of the deep compare.
9837 *
9838 * @note Must be called from under this object's write lock. Locks children for
9839 * writing.
9840 *
9841 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9842 * initialized to false and that will be set to true by this function if
9843 * the caller must invoke VirtualBox::saveSettings() because the global
9844 * settings have changed. This will happen if a machine rename has been
9845 * saved and the global machine and media registries will therefore need
9846 * updating.
9847 */
9848HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9849 int aFlags /*= 0*/)
9850{
9851 LogFlowThisFuncEnter();
9852
9853 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9854
9855 /* make sure child objects are unable to modify the settings while we are
9856 * saving them */
9857 ensureNoStateDependencies();
9858
9859 AssertReturn(!isSnapshotMachine(),
9860 E_FAIL);
9861
9862 HRESULT rc = S_OK;
9863 bool fNeedsWrite = false;
9864
9865 /* First, prepare to save settings. It will care about renaming the
9866 * settings directory and file if the machine name was changed and about
9867 * creating a new settings file if this is a new machine. */
9868 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9869 if (FAILED(rc)) return rc;
9870
9871 // keep a pointer to the current settings structures
9872 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9873 settings::MachineConfigFile *pNewConfig = NULL;
9874
9875 try
9876 {
9877 // make a fresh one to have everyone write stuff into
9878 pNewConfig = new settings::MachineConfigFile(NULL);
9879 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9880
9881 // now go and copy all the settings data from COM to the settings structures
9882 // (this calles saveSettings() on all the COM objects in the machine)
9883 copyMachineDataToSettings(*pNewConfig);
9884
9885 if (aFlags & SaveS_ResetCurStateModified)
9886 {
9887 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9888 mData->mCurrentStateModified = FALSE;
9889 fNeedsWrite = true; // always, no need to compare
9890 }
9891 else if (aFlags & SaveS_Force)
9892 {
9893 fNeedsWrite = true; // always, no need to compare
9894 }
9895 else
9896 {
9897 if (!mData->mCurrentStateModified)
9898 {
9899 // do a deep compare of the settings that we just saved with the settings
9900 // previously stored in the config file; this invokes MachineConfigFile::operator==
9901 // which does a deep compare of all the settings, which is expensive but less expensive
9902 // than writing out XML in vain
9903 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9904
9905 // could still be modified if any settings changed
9906 mData->mCurrentStateModified = fAnySettingsChanged;
9907
9908 fNeedsWrite = fAnySettingsChanged;
9909 }
9910 else
9911 fNeedsWrite = true;
9912 }
9913
9914 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9915
9916 if (fNeedsWrite)
9917 // now spit it all out!
9918 pNewConfig->write(mData->m_strConfigFileFull);
9919
9920 mData->pMachineConfigFile = pNewConfig;
9921 delete pOldConfig;
9922 commit();
9923
9924 // after saving settings, we are no longer different from the XML on disk
9925 mData->flModifications = 0;
9926 }
9927 catch (HRESULT err)
9928 {
9929 // we assume that error info is set by the thrower
9930 rc = err;
9931
9932 // restore old config
9933 delete pNewConfig;
9934 mData->pMachineConfigFile = pOldConfig;
9935 }
9936 catch (...)
9937 {
9938 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9939 }
9940
9941 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9942 {
9943 /* Fire the data change event, even on failure (since we've already
9944 * committed all data). This is done only for SessionMachines because
9945 * mutable Machine instances are always not registered (i.e. private
9946 * to the client process that creates them) and thus don't need to
9947 * inform callbacks. */
9948 if (isSessionMachine())
9949 mParent->onMachineDataChange(mData->mUuid);
9950 }
9951
9952 LogFlowThisFunc(("rc=%08X\n", rc));
9953 LogFlowThisFuncLeave();
9954 return rc;
9955}
9956
9957/**
9958 * Implementation for saving the machine settings into the given
9959 * settings::MachineConfigFile instance. This copies machine extradata
9960 * from the previous machine config file in the instance data, if any.
9961 *
9962 * This gets called from two locations:
9963 *
9964 * -- Machine::saveSettings(), during the regular XML writing;
9965 *
9966 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9967 * exported to OVF and we write the VirtualBox proprietary XML
9968 * into a <vbox:Machine> tag.
9969 *
9970 * This routine fills all the fields in there, including snapshots, *except*
9971 * for the following:
9972 *
9973 * -- fCurrentStateModified. There is some special logic associated with that.
9974 *
9975 * The caller can then call MachineConfigFile::write() or do something else
9976 * with it.
9977 *
9978 * Caller must hold the machine lock!
9979 *
9980 * This throws XML errors and HRESULT, so the caller must have a catch block!
9981 */
9982void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9983{
9984 // deep copy extradata
9985 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9986
9987 config.uuid = mData->mUuid;
9988
9989 // copy name, description, OS type, teleport, UTC etc.
9990 config.machineUserData = mUserData->s;
9991
9992 // Encode the Icon Override data from Machine and store on config userdata.
9993 com::SafeArray<BYTE> iconByte;
9994 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
9995 ssize_t cbData = iconByte.size();
9996 if (cbData > 0)
9997 {
9998 ssize_t cchOut = RTBase64EncodedLength(cbData);
9999 Utf8Str strIconData;
10000 strIconData.reserve(cchOut+1);
10001 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10002 strIconData.mutableRaw(), strIconData.capacity(),
10003 NULL);
10004 if (RT_FAILURE(vrc))
10005 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10006 strIconData.jolt();
10007 config.machineUserData.ovIcon = strIconData;
10008 }
10009 else
10010 config.machineUserData.ovIcon.setNull();
10011
10012 if ( mData->mMachineState == MachineState_Saved
10013 || mData->mMachineState == MachineState_Restoring
10014 // when deleting a snapshot we may or may not have a saved state in the current state,
10015 // so let's not assert here please
10016 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10017 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10018 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10019 && (!mSSData->strStateFilePath.isEmpty())
10020 )
10021 )
10022 {
10023 Assert(!mSSData->strStateFilePath.isEmpty());
10024 /* try to make the file name relative to the settings file dir */
10025 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10026 }
10027 else
10028 {
10029 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10030 config.strStateFile.setNull();
10031 }
10032
10033 if (mData->mCurrentSnapshot)
10034 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10035 else
10036 config.uuidCurrentSnapshot.clear();
10037
10038 config.timeLastStateChange = mData->mLastStateChange;
10039 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10040 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10041
10042 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10043 if (FAILED(rc)) throw rc;
10044
10045 rc = saveStorageControllers(config.storageMachine);
10046 if (FAILED(rc)) throw rc;
10047
10048 // save machine's media registry if this is VirtualBox 4.0 or later
10049 if (config.canHaveOwnMediaRegistry())
10050 {
10051 // determine machine folder
10052 Utf8Str strMachineFolder = getSettingsFileFull();
10053 strMachineFolder.stripFilename();
10054 mParent->saveMediaRegistry(config.mediaRegistry,
10055 getId(), // only media with registry ID == machine UUID
10056 strMachineFolder);
10057 // this throws HRESULT
10058 }
10059
10060 // save snapshots
10061 rc = saveAllSnapshots(config);
10062 if (FAILED(rc)) throw rc;
10063}
10064
10065/**
10066 * Saves all snapshots of the machine into the given machine config file. Called
10067 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10068 * @param config
10069 * @return
10070 */
10071HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10072{
10073 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10074
10075 HRESULT rc = S_OK;
10076
10077 try
10078 {
10079 config.llFirstSnapshot.clear();
10080
10081 if (mData->mFirstSnapshot)
10082 {
10083 settings::Snapshot snapNew;
10084 config.llFirstSnapshot.push_back(snapNew);
10085
10086 // get reference to the fresh copy of the snapshot on the list and
10087 // work on that copy directly to avoid excessive copying later
10088 settings::Snapshot &snap = config.llFirstSnapshot.front();
10089
10090 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10091 if (FAILED(rc)) throw rc;
10092 }
10093
10094// if (mType == IsSessionMachine)
10095// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10096
10097 }
10098 catch (HRESULT err)
10099 {
10100 /* we assume that error info is set by the thrower */
10101 rc = err;
10102 }
10103 catch (...)
10104 {
10105 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10106 }
10107
10108 return rc;
10109}
10110
10111/**
10112 * Saves the VM hardware configuration. It is assumed that the
10113 * given node is empty.
10114 *
10115 * @param data Reference to the settings object for the hardware config.
10116 * @param pDbg Pointer to the settings object for the debugging config
10117 * which happens to live in mHWData.
10118 * @param pAutostart Pointer to the settings object for the autostart config
10119 * which happens to live in mHWData.
10120 */
10121HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10122 settings::Autostart *pAutostart)
10123{
10124 HRESULT rc = S_OK;
10125
10126 try
10127 {
10128 /* The hardware version attribute (optional).
10129 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10130 if ( mHWData->mHWVersion == "1"
10131 && mSSData->strStateFilePath.isEmpty()
10132 )
10133 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. */
10134
10135 data.strVersion = mHWData->mHWVersion;
10136 data.uuid = mHWData->mHardwareUUID;
10137
10138 // CPU
10139 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10140 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10141 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10142 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10143 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10144 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10145 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10146 data.fPAE = !!mHWData->mPAEEnabled;
10147 data.enmLongMode = mHWData->mLongMode;
10148 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10149
10150 /* Standard and Extended CPUID leafs. */
10151 data.llCpuIdLeafs.clear();
10152 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10153 {
10154 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10155 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10156 }
10157 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10158 {
10159 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10160 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10161 }
10162
10163 data.cCPUs = mHWData->mCPUCount;
10164 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10165 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10166
10167 data.llCpus.clear();
10168 if (data.fCpuHotPlug)
10169 {
10170 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10171 {
10172 if (mHWData->mCPUAttached[idx])
10173 {
10174 settings::Cpu cpu;
10175 cpu.ulId = idx;
10176 data.llCpus.push_back(cpu);
10177 }
10178 }
10179 }
10180
10181 // memory
10182 data.ulMemorySizeMB = mHWData->mMemorySize;
10183 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10184
10185 // firmware
10186 data.firmwareType = mHWData->mFirmwareType;
10187
10188 // HID
10189 data.pointingHIDType = mHWData->mPointingHIDType;
10190 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10191
10192 // chipset
10193 data.chipsetType = mHWData->mChipsetType;
10194
10195 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10196 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10197
10198 // HPET
10199 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10200
10201 // boot order
10202 data.mapBootOrder.clear();
10203 for (size_t i = 0;
10204 i < RT_ELEMENTS(mHWData->mBootOrder);
10205 ++i)
10206 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10207
10208 // display
10209 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10210 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10211 data.cMonitors = mHWData->mMonitorCount;
10212 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10213 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10214 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10215 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10216 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10217 data.ulVideoCaptureFps = mHWData->mVideoCaptureFps;
10218 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10219 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10220 {
10221 if (mHWData->maVideoCaptureScreens[i])
10222 ASMBitSet(&data.u64VideoCaptureScreens, i);
10223 else
10224 ASMBitClear(&data.u64VideoCaptureScreens, i);
10225 }
10226 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
10227
10228 /* VRDEServer settings (optional) */
10229 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10230 if (FAILED(rc)) throw rc;
10231
10232 /* BIOS (required) */
10233 rc = mBIOSSettings->saveSettings(data.biosSettings);
10234 if (FAILED(rc)) throw rc;
10235
10236 /* USB Controller (required) */
10237 rc = mUSBController->saveSettings(data.usbController);
10238 if (FAILED(rc)) throw rc;
10239
10240 /* Network adapters (required) */
10241 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10242 data.llNetworkAdapters.clear();
10243 /* Write out only the nominal number of network adapters for this
10244 * chipset type. Since Machine::commit() hasn't been called there
10245 * may be extra NIC settings in the vector. */
10246 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10247 {
10248 settings::NetworkAdapter nic;
10249 nic.ulSlot = slot;
10250 /* paranoia check... must not be NULL, but must not crash either. */
10251 if (mNetworkAdapters[slot])
10252 {
10253 rc = mNetworkAdapters[slot]->saveSettings(nic);
10254 if (FAILED(rc)) throw rc;
10255
10256 data.llNetworkAdapters.push_back(nic);
10257 }
10258 }
10259
10260 /* Serial ports */
10261 data.llSerialPorts.clear();
10262 for (ULONG slot = 0;
10263 slot < RT_ELEMENTS(mSerialPorts);
10264 ++slot)
10265 {
10266 settings::SerialPort s;
10267 s.ulSlot = slot;
10268 rc = mSerialPorts[slot]->saveSettings(s);
10269 if (FAILED(rc)) return rc;
10270
10271 data.llSerialPorts.push_back(s);
10272 }
10273
10274 /* Parallel ports */
10275 data.llParallelPorts.clear();
10276 for (ULONG slot = 0;
10277 slot < RT_ELEMENTS(mParallelPorts);
10278 ++slot)
10279 {
10280 settings::ParallelPort p;
10281 p.ulSlot = slot;
10282 rc = mParallelPorts[slot]->saveSettings(p);
10283 if (FAILED(rc)) return rc;
10284
10285 data.llParallelPorts.push_back(p);
10286 }
10287
10288 /* Audio adapter */
10289 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10290 if (FAILED(rc)) return rc;
10291
10292 /* Shared folders */
10293 data.llSharedFolders.clear();
10294 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10295 it != mHWData->mSharedFolders.end();
10296 ++it)
10297 {
10298 SharedFolder *pSF = *it;
10299 AutoCaller sfCaller(pSF);
10300 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10301 settings::SharedFolder sf;
10302 sf.strName = pSF->getName();
10303 sf.strHostPath = pSF->getHostPath();
10304 sf.fWritable = !!pSF->isWritable();
10305 sf.fAutoMount = !!pSF->isAutoMounted();
10306
10307 data.llSharedFolders.push_back(sf);
10308 }
10309
10310 // clipboard
10311 data.clipboardMode = mHWData->mClipboardMode;
10312
10313 // drag'n'drop
10314 data.dragAndDropMode = mHWData->mDragAndDropMode;
10315
10316 /* Guest */
10317 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10318
10319 // IO settings
10320 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10321 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10322
10323 /* BandwidthControl (required) */
10324 rc = mBandwidthControl->saveSettings(data.ioSettings);
10325 if (FAILED(rc)) throw rc;
10326
10327 /* Host PCI devices */
10328 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10329 it != mHWData->mPCIDeviceAssignments.end();
10330 ++it)
10331 {
10332 ComObjPtr<PCIDeviceAttachment> pda = *it;
10333 settings::HostPCIDeviceAttachment hpda;
10334
10335 rc = pda->saveSettings(hpda);
10336 if (FAILED(rc)) throw rc;
10337
10338 data.pciAttachments.push_back(hpda);
10339 }
10340
10341
10342 // guest properties
10343 data.llGuestProperties.clear();
10344#ifdef VBOX_WITH_GUEST_PROPS
10345 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10346 it != mHWData->mGuestProperties.end();
10347 ++it)
10348 {
10349 HWData::GuestProperty property = it->second;
10350
10351 /* Remove transient guest properties at shutdown unless we
10352 * are saving state */
10353 if ( ( mData->mMachineState == MachineState_PoweredOff
10354 || mData->mMachineState == MachineState_Aborted
10355 || mData->mMachineState == MachineState_Teleported)
10356 && ( property.mFlags & guestProp::TRANSIENT
10357 || property.mFlags & guestProp::TRANSRESET))
10358 continue;
10359 settings::GuestProperty prop;
10360 prop.strName = it->first;
10361 prop.strValue = property.strValue;
10362 prop.timestamp = property.mTimestamp;
10363 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10364 guestProp::writeFlags(property.mFlags, szFlags);
10365 prop.strFlags = szFlags;
10366
10367 data.llGuestProperties.push_back(prop);
10368 }
10369
10370 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10371 /* I presume this doesn't require a backup(). */
10372 mData->mGuestPropertiesModified = FALSE;
10373#endif /* VBOX_WITH_GUEST_PROPS defined */
10374
10375 *pDbg = mHWData->mDebugging;
10376 *pAutostart = mHWData->mAutostart;
10377
10378 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10379 }
10380 catch(std::bad_alloc &)
10381 {
10382 return E_OUTOFMEMORY;
10383 }
10384
10385 AssertComRC(rc);
10386 return rc;
10387}
10388
10389/**
10390 * Saves the storage controller configuration.
10391 *
10392 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10393 */
10394HRESULT Machine::saveStorageControllers(settings::Storage &data)
10395{
10396 data.llStorageControllers.clear();
10397
10398 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10399 it != mStorageControllers->end();
10400 ++it)
10401 {
10402 HRESULT rc;
10403 ComObjPtr<StorageController> pCtl = *it;
10404
10405 settings::StorageController ctl;
10406 ctl.strName = pCtl->getName();
10407 ctl.controllerType = pCtl->getControllerType();
10408 ctl.storageBus = pCtl->getStorageBus();
10409 ctl.ulInstance = pCtl->getInstance();
10410 ctl.fBootable = pCtl->getBootable();
10411
10412 /* Save the port count. */
10413 ULONG portCount;
10414 rc = pCtl->COMGETTER(PortCount)(&portCount);
10415 ComAssertComRCRet(rc, rc);
10416 ctl.ulPortCount = portCount;
10417
10418 /* Save fUseHostIOCache */
10419 BOOL fUseHostIOCache;
10420 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10421 ComAssertComRCRet(rc, rc);
10422 ctl.fUseHostIOCache = !!fUseHostIOCache;
10423
10424 /* Save IDE emulation settings. */
10425 if (ctl.controllerType == StorageControllerType_IntelAhci)
10426 {
10427 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10428 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10429 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10430 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10431 )
10432 ComAssertComRCRet(rc, rc);
10433 }
10434
10435 /* save the devices now. */
10436 rc = saveStorageDevices(pCtl, ctl);
10437 ComAssertComRCRet(rc, rc);
10438
10439 data.llStorageControllers.push_back(ctl);
10440 }
10441
10442 return S_OK;
10443}
10444
10445/**
10446 * Saves the hard disk configuration.
10447 */
10448HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10449 settings::StorageController &data)
10450{
10451 MediaData::AttachmentList atts;
10452
10453 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10454 if (FAILED(rc)) return rc;
10455
10456 data.llAttachedDevices.clear();
10457 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10458 it != atts.end();
10459 ++it)
10460 {
10461 settings::AttachedDevice dev;
10462
10463 MediumAttachment *pAttach = *it;
10464 Medium *pMedium = pAttach->getMedium();
10465
10466 dev.deviceType = pAttach->getType();
10467 dev.lPort = pAttach->getPort();
10468 dev.lDevice = pAttach->getDevice();
10469 if (pMedium)
10470 {
10471 if (pMedium->isHostDrive())
10472 dev.strHostDriveSrc = pMedium->getLocationFull();
10473 else
10474 dev.uuid = pMedium->getId();
10475 dev.fPassThrough = pAttach->getPassthrough();
10476 dev.fTempEject = pAttach->getTempEject();
10477 dev.fNonRotational = pAttach->getNonRotational();
10478 dev.fDiscard = pAttach->getDiscard();
10479 }
10480
10481 dev.strBwGroup = pAttach->getBandwidthGroup();
10482
10483 data.llAttachedDevices.push_back(dev);
10484 }
10485
10486 return S_OK;
10487}
10488
10489/**
10490 * Saves machine state settings as defined by aFlags
10491 * (SaveSTS_* values).
10492 *
10493 * @param aFlags Combination of SaveSTS_* flags.
10494 *
10495 * @note Locks objects for writing.
10496 */
10497HRESULT Machine::saveStateSettings(int aFlags)
10498{
10499 if (aFlags == 0)
10500 return S_OK;
10501
10502 AutoCaller autoCaller(this);
10503 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10504
10505 /* This object's write lock is also necessary to serialize file access
10506 * (prevent concurrent reads and writes) */
10507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10508
10509 HRESULT rc = S_OK;
10510
10511 Assert(mData->pMachineConfigFile);
10512
10513 try
10514 {
10515 if (aFlags & SaveSTS_CurStateModified)
10516 mData->pMachineConfigFile->fCurrentStateModified = true;
10517
10518 if (aFlags & SaveSTS_StateFilePath)
10519 {
10520 if (!mSSData->strStateFilePath.isEmpty())
10521 /* try to make the file name relative to the settings file dir */
10522 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10523 else
10524 mData->pMachineConfigFile->strStateFile.setNull();
10525 }
10526
10527 if (aFlags & SaveSTS_StateTimeStamp)
10528 {
10529 Assert( mData->mMachineState != MachineState_Aborted
10530 || mSSData->strStateFilePath.isEmpty());
10531
10532 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10533
10534 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10535//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10536 }
10537
10538 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10539 }
10540 catch (...)
10541 {
10542 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10543 }
10544
10545 return rc;
10546}
10547
10548/**
10549 * Ensures that the given medium is added to a media registry. If this machine
10550 * was created with 4.0 or later, then the machine registry is used. Otherwise
10551 * the global VirtualBox media registry is used.
10552 *
10553 * Caller must NOT hold machine lock, media tree or any medium locks!
10554 *
10555 * @param pMedium
10556 */
10557void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10558{
10559 /* Paranoia checks: do not hold machine or media tree locks. */
10560 AssertReturnVoid(!isWriteLockOnCurrentThread());
10561 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10562
10563 ComObjPtr<Medium> pBase;
10564 {
10565 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10566 pBase = pMedium->getBase();
10567 }
10568
10569 /* Paranoia checks: do not hold medium locks. */
10570 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10571 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10572
10573 // decide which medium registry to use now that the medium is attached:
10574 Guid uuid;
10575 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10576 // machine XML is VirtualBox 4.0 or higher:
10577 uuid = getId(); // machine UUID
10578 else
10579 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10580
10581 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10582 mParent->markRegistryModified(uuid);
10583
10584 /* For more complex hard disk structures it can happen that the base
10585 * medium isn't yet associated with any medium registry. Do that now. */
10586 if (pMedium != pBase)
10587 {
10588 if (pBase->addRegistry(uuid, true /* fRecurse */))
10589 mParent->markRegistryModified(uuid);
10590 }
10591}
10592
10593/**
10594 * Creates differencing hard disks for all normal hard disks attached to this
10595 * machine and a new set of attachments to refer to created disks.
10596 *
10597 * Used when taking a snapshot or when deleting the current state. Gets called
10598 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10599 *
10600 * This method assumes that mMediaData contains the original hard disk attachments
10601 * it needs to create diffs for. On success, these attachments will be replaced
10602 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10603 * called to delete created diffs which will also rollback mMediaData and restore
10604 * whatever was backed up before calling this method.
10605 *
10606 * Attachments with non-normal hard disks are left as is.
10607 *
10608 * If @a aOnline is @c false then the original hard disks that require implicit
10609 * diffs will be locked for reading. Otherwise it is assumed that they are
10610 * already locked for writing (when the VM was started). Note that in the latter
10611 * case it is responsibility of the caller to lock the newly created diffs for
10612 * writing if this method succeeds.
10613 *
10614 * @param aProgress Progress object to run (must contain at least as
10615 * many operations left as the number of hard disks
10616 * attached).
10617 * @param aOnline Whether the VM was online prior to this operation.
10618 *
10619 * @note The progress object is not marked as completed, neither on success nor
10620 * on failure. This is a responsibility of the caller.
10621 *
10622 * @note Locks this object and the media tree for writing.
10623 */
10624HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10625 ULONG aWeight,
10626 bool aOnline)
10627{
10628 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10629
10630 AutoCaller autoCaller(this);
10631 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10632
10633 AutoMultiWriteLock2 alock(this->lockHandle(),
10634 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10635
10636 /* must be in a protective state because we release the lock below */
10637 AssertReturn( mData->mMachineState == MachineState_Saving
10638 || mData->mMachineState == MachineState_LiveSnapshotting
10639 || mData->mMachineState == MachineState_RestoringSnapshot
10640 || mData->mMachineState == MachineState_DeletingSnapshot
10641 , E_FAIL);
10642
10643 HRESULT rc = S_OK;
10644
10645 // use appropriate locked media map (online or offline)
10646 MediumLockListMap lockedMediaOffline;
10647 MediumLockListMap *lockedMediaMap;
10648 if (aOnline)
10649 lockedMediaMap = &mData->mSession.mLockedMedia;
10650 else
10651 lockedMediaMap = &lockedMediaOffline;
10652
10653 try
10654 {
10655 if (!aOnline)
10656 {
10657 /* lock all attached hard disks early to detect "in use"
10658 * situations before creating actual diffs */
10659 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10660 it != mMediaData->mAttachments.end();
10661 ++it)
10662 {
10663 MediumAttachment* pAtt = *it;
10664 if (pAtt->getType() == DeviceType_HardDisk)
10665 {
10666 Medium* pMedium = pAtt->getMedium();
10667 Assert(pMedium);
10668
10669 MediumLockList *pMediumLockList(new MediumLockList());
10670 alock.release();
10671 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10672 false /* fMediumLockWrite */,
10673 NULL,
10674 *pMediumLockList);
10675 alock.acquire();
10676 if (FAILED(rc))
10677 {
10678 delete pMediumLockList;
10679 throw rc;
10680 }
10681 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10682 if (FAILED(rc))
10683 {
10684 throw setError(rc,
10685 tr("Collecting locking information for all attached media failed"));
10686 }
10687 }
10688 }
10689
10690 /* Now lock all media. If this fails, nothing is locked. */
10691 alock.release();
10692 rc = lockedMediaMap->Lock();
10693 alock.acquire();
10694 if (FAILED(rc))
10695 {
10696 throw setError(rc,
10697 tr("Locking of attached media failed"));
10698 }
10699 }
10700
10701 /* remember the current list (note that we don't use backup() since
10702 * mMediaData may be already backed up) */
10703 MediaData::AttachmentList atts = mMediaData->mAttachments;
10704
10705 /* start from scratch */
10706 mMediaData->mAttachments.clear();
10707
10708 /* go through remembered attachments and create diffs for normal hard
10709 * disks and attach them */
10710 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10711 it != atts.end();
10712 ++it)
10713 {
10714 MediumAttachment* pAtt = *it;
10715
10716 DeviceType_T devType = pAtt->getType();
10717 Medium* pMedium = pAtt->getMedium();
10718
10719 if ( devType != DeviceType_HardDisk
10720 || pMedium == NULL
10721 || pMedium->getType() != MediumType_Normal)
10722 {
10723 /* copy the attachment as is */
10724
10725 /** @todo the progress object created in Console::TakeSnaphot
10726 * only expects operations for hard disks. Later other
10727 * device types need to show up in the progress as well. */
10728 if (devType == DeviceType_HardDisk)
10729 {
10730 if (pMedium == NULL)
10731 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10732 aWeight); // weight
10733 else
10734 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10735 pMedium->getBase()->getName().c_str()).raw(),
10736 aWeight); // weight
10737 }
10738
10739 mMediaData->mAttachments.push_back(pAtt);
10740 continue;
10741 }
10742
10743 /* need a diff */
10744 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10745 pMedium->getBase()->getName().c_str()).raw(),
10746 aWeight); // weight
10747
10748 Utf8Str strFullSnapshotFolder;
10749 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10750
10751 ComObjPtr<Medium> diff;
10752 diff.createObject();
10753 // store the diff in the same registry as the parent
10754 // (this cannot fail here because we can't create implicit diffs for
10755 // unregistered images)
10756 Guid uuidRegistryParent;
10757 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10758 Assert(fInRegistry); NOREF(fInRegistry);
10759 rc = diff->init(mParent,
10760 pMedium->getPreferredDiffFormat(),
10761 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10762 uuidRegistryParent);
10763 if (FAILED(rc)) throw rc;
10764
10765 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10766 * the push_back? Looks like we're going to release medium with the
10767 * wrong kind of lock (general issue with if we fail anywhere at all)
10768 * and an orphaned VDI in the snapshots folder. */
10769
10770 /* update the appropriate lock list */
10771 MediumLockList *pMediumLockList;
10772 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10773 AssertComRCThrowRC(rc);
10774 if (aOnline)
10775 {
10776 alock.release();
10777 /* The currently attached medium will be read-only, change
10778 * the lock type to read. */
10779 rc = pMediumLockList->Update(pMedium, false);
10780 alock.acquire();
10781 AssertComRCThrowRC(rc);
10782 }
10783
10784 /* release the locks before the potentially lengthy operation */
10785 alock.release();
10786 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10787 pMediumLockList,
10788 NULL /* aProgress */,
10789 true /* aWait */);
10790 alock.acquire();
10791 if (FAILED(rc)) throw rc;
10792
10793 /* actual lock list update is done in Medium::commitMedia */
10794
10795 rc = diff->addBackReference(mData->mUuid);
10796 AssertComRCThrowRC(rc);
10797
10798 /* add a new attachment */
10799 ComObjPtr<MediumAttachment> attachment;
10800 attachment.createObject();
10801 rc = attachment->init(this,
10802 diff,
10803 pAtt->getControllerName(),
10804 pAtt->getPort(),
10805 pAtt->getDevice(),
10806 DeviceType_HardDisk,
10807 true /* aImplicit */,
10808 false /* aPassthrough */,
10809 false /* aTempEject */,
10810 pAtt->getNonRotational(),
10811 pAtt->getDiscard(),
10812 pAtt->getBandwidthGroup());
10813 if (FAILED(rc)) throw rc;
10814
10815 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10816 AssertComRCThrowRC(rc);
10817 mMediaData->mAttachments.push_back(attachment);
10818 }
10819 }
10820 catch (HRESULT aRC) { rc = aRC; }
10821
10822 /* unlock all hard disks we locked when there is no VM */
10823 if (!aOnline)
10824 {
10825 ErrorInfoKeeper eik;
10826
10827 HRESULT rc1 = lockedMediaMap->Clear();
10828 AssertComRC(rc1);
10829 }
10830
10831 return rc;
10832}
10833
10834/**
10835 * Deletes implicit differencing hard disks created either by
10836 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10837 *
10838 * Note that to delete hard disks created by #AttachDevice() this method is
10839 * called from #fixupMedia() when the changes are rolled back.
10840 *
10841 * @note Locks this object and the media tree for writing.
10842 */
10843HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10844{
10845 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10846
10847 AutoCaller autoCaller(this);
10848 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10849
10850 AutoMultiWriteLock2 alock(this->lockHandle(),
10851 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10852
10853 /* We absolutely must have backed up state. */
10854 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10855
10856 /* Check if there are any implicitly created diff images. */
10857 bool fImplicitDiffs = false;
10858 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10859 it != mMediaData->mAttachments.end();
10860 ++it)
10861 {
10862 const ComObjPtr<MediumAttachment> &pAtt = *it;
10863 if (pAtt->isImplicit())
10864 {
10865 fImplicitDiffs = true;
10866 break;
10867 }
10868 }
10869 /* If there is nothing to do, leave early. This saves lots of image locking
10870 * effort. It also avoids a MachineStateChanged event without real reason.
10871 * This is important e.g. when loading a VM config, because there should be
10872 * no events. Otherwise API clients can become thoroughly confused for
10873 * inaccessible VMs (the code for loading VM configs uses this method for
10874 * cleanup if the config makes no sense), as they take such events as an
10875 * indication that the VM is alive, and they would force the VM config to
10876 * be reread, leading to an endless loop. */
10877 if (!fImplicitDiffs)
10878 return S_OK;
10879
10880 HRESULT rc = S_OK;
10881 MachineState_T oldState = mData->mMachineState;
10882
10883 /* will release the lock before the potentially lengthy operation,
10884 * so protect with the special state (unless already protected) */
10885 if ( oldState != MachineState_Saving
10886 && oldState != MachineState_LiveSnapshotting
10887 && oldState != MachineState_RestoringSnapshot
10888 && oldState != MachineState_DeletingSnapshot
10889 && oldState != MachineState_DeletingSnapshotOnline
10890 && oldState != MachineState_DeletingSnapshotPaused
10891 )
10892 setMachineState(MachineState_SettingUp);
10893
10894 // use appropriate locked media map (online or offline)
10895 MediumLockListMap lockedMediaOffline;
10896 MediumLockListMap *lockedMediaMap;
10897 if (aOnline)
10898 lockedMediaMap = &mData->mSession.mLockedMedia;
10899 else
10900 lockedMediaMap = &lockedMediaOffline;
10901
10902 try
10903 {
10904 if (!aOnline)
10905 {
10906 /* lock all attached hard disks early to detect "in use"
10907 * situations before deleting actual diffs */
10908 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10909 it != mMediaData->mAttachments.end();
10910 ++it)
10911 {
10912 MediumAttachment* pAtt = *it;
10913 if (pAtt->getType() == DeviceType_HardDisk)
10914 {
10915 Medium* pMedium = pAtt->getMedium();
10916 Assert(pMedium);
10917
10918 MediumLockList *pMediumLockList(new MediumLockList());
10919 alock.release();
10920 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10921 false /* fMediumLockWrite */,
10922 NULL,
10923 *pMediumLockList);
10924 alock.acquire();
10925
10926 if (FAILED(rc))
10927 {
10928 delete pMediumLockList;
10929 throw rc;
10930 }
10931
10932 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10933 if (FAILED(rc))
10934 throw rc;
10935 }
10936 }
10937
10938 if (FAILED(rc))
10939 throw rc;
10940 } // end of offline
10941
10942 /* Lock lists are now up to date and include implicitly created media */
10943
10944 /* Go through remembered attachments and delete all implicitly created
10945 * diffs and fix up the attachment information */
10946 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10947 MediaData::AttachmentList implicitAtts;
10948 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10949 it != mMediaData->mAttachments.end();
10950 ++it)
10951 {
10952 ComObjPtr<MediumAttachment> pAtt = *it;
10953 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10954 if (pMedium.isNull())
10955 continue;
10956
10957 // Implicit attachments go on the list for deletion and back references are removed.
10958 if (pAtt->isImplicit())
10959 {
10960 /* Deassociate and mark for deletion */
10961 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10962 rc = pMedium->removeBackReference(mData->mUuid);
10963 if (FAILED(rc))
10964 throw rc;
10965 implicitAtts.push_back(pAtt);
10966 continue;
10967 }
10968
10969 /* Was this medium attached before? */
10970 if (!findAttachment(oldAtts, pMedium))
10971 {
10972 /* no: de-associate */
10973 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10974 rc = pMedium->removeBackReference(mData->mUuid);
10975 if (FAILED(rc))
10976 throw rc;
10977 continue;
10978 }
10979 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10980 }
10981
10982 /* If there are implicit attachments to delete, throw away the lock
10983 * map contents (which will unlock all media) since the medium
10984 * attachments will be rolled back. Below we need to completely
10985 * recreate the lock map anyway since it is infinitely complex to
10986 * do this incrementally (would need reconstructing each attachment
10987 * change, which would be extremely hairy). */
10988 if (implicitAtts.size() != 0)
10989 {
10990 ErrorInfoKeeper eik;
10991
10992 HRESULT rc1 = lockedMediaMap->Clear();
10993 AssertComRC(rc1);
10994 }
10995
10996 /* rollback hard disk changes */
10997 mMediaData.rollback();
10998
10999 MultiResult mrc(S_OK);
11000
11001 // Delete unused implicit diffs.
11002 if (implicitAtts.size() != 0)
11003 {
11004 alock.release();
11005
11006 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11007 it != implicitAtts.end();
11008 ++it)
11009 {
11010 // Remove medium associated with this attachment.
11011 ComObjPtr<MediumAttachment> pAtt = *it;
11012 Assert(pAtt);
11013 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11014 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11015 Assert(pMedium);
11016
11017 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11018 // continue on delete failure, just collect error messages
11019 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11020 mrc = rc;
11021 }
11022
11023 alock.acquire();
11024
11025 /* if there is a VM recreate media lock map as mentioned above,
11026 * otherwise it is a waste of time and we leave things unlocked */
11027 if (aOnline)
11028 {
11029 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11030 /* must never be NULL, but better safe than sorry */
11031 if (!pMachine.isNull())
11032 {
11033 alock.release();
11034 rc = mData->mSession.mMachine->lockMedia();
11035 alock.acquire();
11036 if (FAILED(rc))
11037 throw rc;
11038 }
11039 }
11040 }
11041 }
11042 catch (HRESULT aRC) {rc = aRC;}
11043
11044 if (mData->mMachineState == MachineState_SettingUp)
11045 setMachineState(oldState);
11046
11047 /* unlock all hard disks we locked when there is no VM */
11048 if (!aOnline)
11049 {
11050 ErrorInfoKeeper eik;
11051
11052 HRESULT rc1 = lockedMediaMap->Clear();
11053 AssertComRC(rc1);
11054 }
11055
11056 return rc;
11057}
11058
11059
11060/**
11061 * Looks through the given list of media attachments for one with the given parameters
11062 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11063 * can be searched as well if needed.
11064 *
11065 * @param list
11066 * @param aControllerName
11067 * @param aControllerPort
11068 * @param aDevice
11069 * @return
11070 */
11071MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11072 IN_BSTR aControllerName,
11073 LONG aControllerPort,
11074 LONG aDevice)
11075{
11076 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11077 it != ll.end();
11078 ++it)
11079 {
11080 MediumAttachment *pAttach = *it;
11081 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11082 return pAttach;
11083 }
11084
11085 return NULL;
11086}
11087
11088/**
11089 * Looks through the given list of media attachments for one with the given parameters
11090 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11091 * can be searched as well if needed.
11092 *
11093 * @param list
11094 * @param aControllerName
11095 * @param aControllerPort
11096 * @param aDevice
11097 * @return
11098 */
11099MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11100 ComObjPtr<Medium> pMedium)
11101{
11102 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11103 it != ll.end();
11104 ++it)
11105 {
11106 MediumAttachment *pAttach = *it;
11107 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11108 if (pMediumThis == pMedium)
11109 return pAttach;
11110 }
11111
11112 return NULL;
11113}
11114
11115/**
11116 * Looks through the given list of media attachments for one with the given parameters
11117 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11118 * can be searched as well if needed.
11119 *
11120 * @param list
11121 * @param aControllerName
11122 * @param aControllerPort
11123 * @param aDevice
11124 * @return
11125 */
11126MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11127 Guid &id)
11128{
11129 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11130 it != ll.end();
11131 ++it)
11132 {
11133 MediumAttachment *pAttach = *it;
11134 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11135 if (pMediumThis->getId() == id)
11136 return pAttach;
11137 }
11138
11139 return NULL;
11140}
11141
11142/**
11143 * Main implementation for Machine::DetachDevice. This also gets called
11144 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11145 *
11146 * @param pAttach Medium attachment to detach.
11147 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11148 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11149 * @return
11150 */
11151HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11152 AutoWriteLock &writeLock,
11153 Snapshot *pSnapshot)
11154{
11155 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11156 DeviceType_T mediumType = pAttach->getType();
11157
11158 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11159
11160 if (pAttach->isImplicit())
11161 {
11162 /* attempt to implicitly delete the implicitly created diff */
11163
11164 /// @todo move the implicit flag from MediumAttachment to Medium
11165 /// and forbid any hard disk operation when it is implicit. Or maybe
11166 /// a special media state for it to make it even more simple.
11167
11168 Assert(mMediaData.isBackedUp());
11169
11170 /* will release the lock before the potentially lengthy operation, so
11171 * protect with the special state */
11172 MachineState_T oldState = mData->mMachineState;
11173 setMachineState(MachineState_SettingUp);
11174
11175 writeLock.release();
11176
11177 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11178 true /*aWait*/);
11179
11180 writeLock.acquire();
11181
11182 setMachineState(oldState);
11183
11184 if (FAILED(rc)) return rc;
11185 }
11186
11187 setModified(IsModified_Storage);
11188 mMediaData.backup();
11189 mMediaData->mAttachments.remove(pAttach);
11190
11191 if (!oldmedium.isNull())
11192 {
11193 // if this is from a snapshot, do not defer detachment to commitMedia()
11194 if (pSnapshot)
11195 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11196 // else if non-hard disk media, do not defer detachment to commitMedia() either
11197 else if (mediumType != DeviceType_HardDisk)
11198 oldmedium->removeBackReference(mData->mUuid);
11199 }
11200
11201 return S_OK;
11202}
11203
11204/**
11205 * Goes thru all media of the given list and
11206 *
11207 * 1) calls detachDevice() on each of them for this machine and
11208 * 2) adds all Medium objects found in the process to the given list,
11209 * depending on cleanupMode.
11210 *
11211 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11212 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11213 * media to the list.
11214 *
11215 * This gets called from Machine::Unregister, both for the actual Machine and
11216 * the SnapshotMachine objects that might be found in the snapshots.
11217 *
11218 * Requires caller and locking. The machine lock must be passed in because it
11219 * will be passed on to detachDevice which needs it for temporary unlocking.
11220 *
11221 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11222 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11223 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11224 * otherwise no media get added.
11225 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11226 * @return
11227 */
11228HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11229 Snapshot *pSnapshot,
11230 CleanupMode_T cleanupMode,
11231 MediaList &llMedia)
11232{
11233 Assert(isWriteLockOnCurrentThread());
11234
11235 HRESULT rc;
11236
11237 // make a temporary list because detachDevice invalidates iterators into
11238 // mMediaData->mAttachments
11239 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11240
11241 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11242 it != llAttachments2.end();
11243 ++it)
11244 {
11245 ComObjPtr<MediumAttachment> &pAttach = *it;
11246 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11247
11248 if (!pMedium.isNull())
11249 {
11250 AutoCaller mac(pMedium);
11251 if (FAILED(mac.rc())) return mac.rc();
11252 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11253 DeviceType_T devType = pMedium->getDeviceType();
11254 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11255 && devType == DeviceType_HardDisk)
11256 || (cleanupMode == CleanupMode_Full)
11257 )
11258 {
11259 llMedia.push_back(pMedium);
11260 ComObjPtr<Medium> pParent = pMedium->getParent();
11261 /*
11262 * Search for medias which are not attached to any machine, but
11263 * in the chain to an attached disk. Mediums are only consided
11264 * if they are:
11265 * - have only one child
11266 * - no references to any machines
11267 * - are of normal medium type
11268 */
11269 while (!pParent.isNull())
11270 {
11271 AutoCaller mac1(pParent);
11272 if (FAILED(mac1.rc())) return mac1.rc();
11273 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11274 if (pParent->getChildren().size() == 1)
11275 {
11276 if ( pParent->getMachineBackRefCount() == 0
11277 && pParent->getType() == MediumType_Normal
11278 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11279 llMedia.push_back(pParent);
11280 }
11281 else
11282 break;
11283 pParent = pParent->getParent();
11284 }
11285 }
11286 }
11287
11288 // real machine: then we need to use the proper method
11289 rc = detachDevice(pAttach, writeLock, pSnapshot);
11290
11291 if (FAILED(rc))
11292 return rc;
11293 }
11294
11295 return S_OK;
11296}
11297
11298/**
11299 * Perform deferred hard disk detachments.
11300 *
11301 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11302 * backed up).
11303 *
11304 * If @a aOnline is @c true then this method will also unlock the old hard disks
11305 * for which the new implicit diffs were created and will lock these new diffs for
11306 * writing.
11307 *
11308 * @param aOnline Whether the VM was online prior to this operation.
11309 *
11310 * @note Locks this object for writing!
11311 */
11312void Machine::commitMedia(bool aOnline /*= false*/)
11313{
11314 AutoCaller autoCaller(this);
11315 AssertComRCReturnVoid(autoCaller.rc());
11316
11317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11318
11319 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11320
11321 HRESULT rc = S_OK;
11322
11323 /* no attach/detach operations -- nothing to do */
11324 if (!mMediaData.isBackedUp())
11325 return;
11326
11327 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11328 bool fMediaNeedsLocking = false;
11329
11330 /* enumerate new attachments */
11331 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11332 it != mMediaData->mAttachments.end();
11333 ++it)
11334 {
11335 MediumAttachment *pAttach = *it;
11336
11337 pAttach->commit();
11338
11339 Medium* pMedium = pAttach->getMedium();
11340 bool fImplicit = pAttach->isImplicit();
11341
11342 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11343 (pMedium) ? pMedium->getName().c_str() : "NULL",
11344 fImplicit));
11345
11346 /** @todo convert all this Machine-based voodoo to MediumAttachment
11347 * based commit logic. */
11348 if (fImplicit)
11349 {
11350 /* convert implicit attachment to normal */
11351 pAttach->setImplicit(false);
11352
11353 if ( aOnline
11354 && pMedium
11355 && pAttach->getType() == DeviceType_HardDisk
11356 )
11357 {
11358 ComObjPtr<Medium> parent = pMedium->getParent();
11359 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11360
11361 /* update the appropriate lock list */
11362 MediumLockList *pMediumLockList;
11363 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11364 AssertComRC(rc);
11365 if (pMediumLockList)
11366 {
11367 /* unlock if there's a need to change the locking */
11368 if (!fMediaNeedsLocking)
11369 {
11370 rc = mData->mSession.mLockedMedia.Unlock();
11371 AssertComRC(rc);
11372 fMediaNeedsLocking = true;
11373 }
11374 rc = pMediumLockList->Update(parent, false);
11375 AssertComRC(rc);
11376 rc = pMediumLockList->Append(pMedium, true);
11377 AssertComRC(rc);
11378 }
11379 }
11380
11381 continue;
11382 }
11383
11384 if (pMedium)
11385 {
11386 /* was this medium attached before? */
11387 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11388 oldIt != oldAtts.end();
11389 ++oldIt)
11390 {
11391 MediumAttachment *pOldAttach = *oldIt;
11392 if (pOldAttach->getMedium() == pMedium)
11393 {
11394 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11395
11396 /* yes: remove from old to avoid de-association */
11397 oldAtts.erase(oldIt);
11398 break;
11399 }
11400 }
11401 }
11402 }
11403
11404 /* enumerate remaining old attachments and de-associate from the
11405 * current machine state */
11406 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11407 it != oldAtts.end();
11408 ++it)
11409 {
11410 MediumAttachment *pAttach = *it;
11411 Medium* pMedium = pAttach->getMedium();
11412
11413 /* Detach only hard disks, since DVD/floppy media is detached
11414 * instantly in MountMedium. */
11415 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11416 {
11417 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11418
11419 /* now de-associate from the current machine state */
11420 rc = pMedium->removeBackReference(mData->mUuid);
11421 AssertComRC(rc);
11422
11423 if (aOnline)
11424 {
11425 /* unlock since medium is not used anymore */
11426 MediumLockList *pMediumLockList;
11427 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11428 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11429 {
11430 /* this happens for online snapshots, there the attachment
11431 * is changing, but only to a diff image created under
11432 * the old one, so there is no separate lock list */
11433 Assert(!pMediumLockList);
11434 }
11435 else
11436 {
11437 AssertComRC(rc);
11438 if (pMediumLockList)
11439 {
11440 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11441 AssertComRC(rc);
11442 }
11443 }
11444 }
11445 }
11446 }
11447
11448 /* take media locks again so that the locking state is consistent */
11449 if (fMediaNeedsLocking)
11450 {
11451 Assert(aOnline);
11452 rc = mData->mSession.mLockedMedia.Lock();
11453 AssertComRC(rc);
11454 }
11455
11456 /* commit the hard disk changes */
11457 mMediaData.commit();
11458
11459 if (isSessionMachine())
11460 {
11461 /*
11462 * Update the parent machine to point to the new owner.
11463 * This is necessary because the stored parent will point to the
11464 * session machine otherwise and cause crashes or errors later
11465 * when the session machine gets invalid.
11466 */
11467 /** @todo Change the MediumAttachment class to behave like any other
11468 * class in this regard by creating peer MediumAttachment
11469 * objects for session machines and share the data with the peer
11470 * machine.
11471 */
11472 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11473 it != mMediaData->mAttachments.end();
11474 ++it)
11475 {
11476 (*it)->updateParentMachine(mPeer);
11477 }
11478
11479 /* attach new data to the primary machine and reshare it */
11480 mPeer->mMediaData.attach(mMediaData);
11481 }
11482
11483 return;
11484}
11485
11486/**
11487 * Perform deferred deletion of implicitly created diffs.
11488 *
11489 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11490 * backed up).
11491 *
11492 * @note Locks this object for writing!
11493 */
11494void Machine::rollbackMedia()
11495{
11496 AutoCaller autoCaller(this);
11497 AssertComRCReturnVoid(autoCaller.rc());
11498
11499 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11500 LogFlowThisFunc(("Entering rollbackMedia\n"));
11501
11502 HRESULT rc = S_OK;
11503
11504 /* no attach/detach operations -- nothing to do */
11505 if (!mMediaData.isBackedUp())
11506 return;
11507
11508 /* enumerate new attachments */
11509 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11510 it != mMediaData->mAttachments.end();
11511 ++it)
11512 {
11513 MediumAttachment *pAttach = *it;
11514 /* Fix up the backrefs for DVD/floppy media. */
11515 if (pAttach->getType() != DeviceType_HardDisk)
11516 {
11517 Medium* pMedium = pAttach->getMedium();
11518 if (pMedium)
11519 {
11520 rc = pMedium->removeBackReference(mData->mUuid);
11521 AssertComRC(rc);
11522 }
11523 }
11524
11525 (*it)->rollback();
11526
11527 pAttach = *it;
11528 /* Fix up the backrefs for DVD/floppy media. */
11529 if (pAttach->getType() != DeviceType_HardDisk)
11530 {
11531 Medium* pMedium = pAttach->getMedium();
11532 if (pMedium)
11533 {
11534 rc = pMedium->addBackReference(mData->mUuid);
11535 AssertComRC(rc);
11536 }
11537 }
11538 }
11539
11540 /** @todo convert all this Machine-based voodoo to MediumAttachment
11541 * based rollback logic. */
11542 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11543
11544 return;
11545}
11546
11547/**
11548 * Returns true if the settings file is located in the directory named exactly
11549 * as the machine; this means, among other things, that the machine directory
11550 * should be auto-renamed.
11551 *
11552 * @param aSettingsDir if not NULL, the full machine settings file directory
11553 * name will be assigned there.
11554 *
11555 * @note Doesn't lock anything.
11556 * @note Not thread safe (must be called from this object's lock).
11557 */
11558bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11559{
11560 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11561 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11562 if (aSettingsDir)
11563 *aSettingsDir = strMachineDirName;
11564 strMachineDirName.stripPath(); // vmname
11565 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11566 strConfigFileOnly.stripPath() // vmname.vbox
11567 .stripExt(); // vmname
11568 /** @todo hack, make somehow use of ComposeMachineFilename */
11569 if (mUserData->s.fDirectoryIncludesUUID)
11570 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11571
11572 AssertReturn(!strMachineDirName.isEmpty(), false);
11573 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11574
11575 return strMachineDirName == strConfigFileOnly;
11576}
11577
11578/**
11579 * Discards all changes to machine settings.
11580 *
11581 * @param aNotify Whether to notify the direct session about changes or not.
11582 *
11583 * @note Locks objects for writing!
11584 */
11585void Machine::rollback(bool aNotify)
11586{
11587 AutoCaller autoCaller(this);
11588 AssertComRCReturn(autoCaller.rc(), (void)0);
11589
11590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11591
11592 if (!mStorageControllers.isNull())
11593 {
11594 if (mStorageControllers.isBackedUp())
11595 {
11596 /* unitialize all new devices (absent in the backed up list). */
11597 StorageControllerList::const_iterator it = mStorageControllers->begin();
11598 StorageControllerList *backedList = mStorageControllers.backedUpData();
11599 while (it != mStorageControllers->end())
11600 {
11601 if ( std::find(backedList->begin(), backedList->end(), *it)
11602 == backedList->end()
11603 )
11604 {
11605 (*it)->uninit();
11606 }
11607 ++it;
11608 }
11609
11610 /* restore the list */
11611 mStorageControllers.rollback();
11612 }
11613
11614 /* rollback any changes to devices after restoring the list */
11615 if (mData->flModifications & IsModified_Storage)
11616 {
11617 StorageControllerList::const_iterator it = mStorageControllers->begin();
11618 while (it != mStorageControllers->end())
11619 {
11620 (*it)->rollback();
11621 ++it;
11622 }
11623 }
11624 }
11625
11626 mUserData.rollback();
11627
11628 mHWData.rollback();
11629
11630 if (mData->flModifications & IsModified_Storage)
11631 rollbackMedia();
11632
11633 if (mBIOSSettings)
11634 mBIOSSettings->rollback();
11635
11636 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11637 mVRDEServer->rollback();
11638
11639 if (mAudioAdapter)
11640 mAudioAdapter->rollback();
11641
11642 if (mUSBController && (mData->flModifications & IsModified_USB))
11643 mUSBController->rollback();
11644
11645 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11646 mBandwidthControl->rollback();
11647
11648 if (!mHWData.isNull())
11649 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11650 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11651 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11652 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11653
11654 if (mData->flModifications & IsModified_NetworkAdapters)
11655 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11656 if ( mNetworkAdapters[slot]
11657 && mNetworkAdapters[slot]->isModified())
11658 {
11659 mNetworkAdapters[slot]->rollback();
11660 networkAdapters[slot] = mNetworkAdapters[slot];
11661 }
11662
11663 if (mData->flModifications & IsModified_SerialPorts)
11664 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11665 if ( mSerialPorts[slot]
11666 && mSerialPorts[slot]->isModified())
11667 {
11668 mSerialPorts[slot]->rollback();
11669 serialPorts[slot] = mSerialPorts[slot];
11670 }
11671
11672 if (mData->flModifications & IsModified_ParallelPorts)
11673 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11674 if ( mParallelPorts[slot]
11675 && mParallelPorts[slot]->isModified())
11676 {
11677 mParallelPorts[slot]->rollback();
11678 parallelPorts[slot] = mParallelPorts[slot];
11679 }
11680
11681 if (aNotify)
11682 {
11683 /* inform the direct session about changes */
11684
11685 ComObjPtr<Machine> that = this;
11686 uint32_t flModifications = mData->flModifications;
11687 alock.release();
11688
11689 if (flModifications & IsModified_SharedFolders)
11690 that->onSharedFolderChange();
11691
11692 if (flModifications & IsModified_VRDEServer)
11693 that->onVRDEServerChange(/* aRestart */ TRUE);
11694 if (flModifications & IsModified_USB)
11695 that->onUSBControllerChange();
11696
11697 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11698 if (networkAdapters[slot])
11699 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11700 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11701 if (serialPorts[slot])
11702 that->onSerialPortChange(serialPorts[slot]);
11703 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11704 if (parallelPorts[slot])
11705 that->onParallelPortChange(parallelPorts[slot]);
11706
11707 if (flModifications & IsModified_Storage)
11708 that->onStorageControllerChange();
11709
11710#if 0
11711 if (flModifications & IsModified_BandwidthControl)
11712 that->onBandwidthControlChange();
11713#endif
11714 }
11715}
11716
11717/**
11718 * Commits all the changes to machine settings.
11719 *
11720 * Note that this operation is supposed to never fail.
11721 *
11722 * @note Locks this object and children for writing.
11723 */
11724void Machine::commit()
11725{
11726 AutoCaller autoCaller(this);
11727 AssertComRCReturnVoid(autoCaller.rc());
11728
11729 AutoCaller peerCaller(mPeer);
11730 AssertComRCReturnVoid(peerCaller.rc());
11731
11732 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11733
11734 /*
11735 * use safe commit to ensure Snapshot machines (that share mUserData)
11736 * will still refer to a valid memory location
11737 */
11738 mUserData.commitCopy();
11739
11740 mHWData.commit();
11741
11742 if (mMediaData.isBackedUp())
11743 commitMedia(Global::IsOnline(mData->mMachineState));
11744
11745 mBIOSSettings->commit();
11746 mVRDEServer->commit();
11747 mAudioAdapter->commit();
11748 mUSBController->commit();
11749 mBandwidthControl->commit();
11750
11751 /* Since mNetworkAdapters is a list which might have been changed (resized)
11752 * without using the Backupable<> template we need to handle the copying
11753 * of the list entries manually, including the creation of peers for the
11754 * new objects. */
11755 bool commitNetworkAdapters = false;
11756 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11757 if (mPeer)
11758 {
11759 /* commit everything, even the ones which will go away */
11760 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11761 mNetworkAdapters[slot]->commit();
11762 /* copy over the new entries, creating a peer and uninit the original */
11763 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11764 for (size_t slot = 0; slot < newSize; slot++)
11765 {
11766 /* look if this adapter has a peer device */
11767 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11768 if (!peer)
11769 {
11770 /* no peer means the adapter is a newly created one;
11771 * create a peer owning data this data share it with */
11772 peer.createObject();
11773 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11774 }
11775 mPeer->mNetworkAdapters[slot] = peer;
11776 }
11777 /* uninit any no longer needed network adapters */
11778 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11779 mNetworkAdapters[slot]->uninit();
11780 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11781 {
11782 if (mPeer->mNetworkAdapters[slot])
11783 mPeer->mNetworkAdapters[slot]->uninit();
11784 }
11785 /* Keep the original network adapter count until this point, so that
11786 * discarding a chipset type change will not lose settings. */
11787 mNetworkAdapters.resize(newSize);
11788 mPeer->mNetworkAdapters.resize(newSize);
11789 }
11790 else
11791 {
11792 /* we have no peer (our parent is the newly created machine);
11793 * just commit changes to the network adapters */
11794 commitNetworkAdapters = true;
11795 }
11796 if (commitNetworkAdapters)
11797 {
11798 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11799 mNetworkAdapters[slot]->commit();
11800 }
11801
11802 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11803 mSerialPorts[slot]->commit();
11804 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11805 mParallelPorts[slot]->commit();
11806
11807 bool commitStorageControllers = false;
11808
11809 if (mStorageControllers.isBackedUp())
11810 {
11811 mStorageControllers.commit();
11812
11813 if (mPeer)
11814 {
11815 /* Commit all changes to new controllers (this will reshare data with
11816 * peers for those who have peers) */
11817 StorageControllerList *newList = new StorageControllerList();
11818 StorageControllerList::const_iterator it = mStorageControllers->begin();
11819 while (it != mStorageControllers->end())
11820 {
11821 (*it)->commit();
11822
11823 /* look if this controller has a peer device */
11824 ComObjPtr<StorageController> peer = (*it)->getPeer();
11825 if (!peer)
11826 {
11827 /* no peer means the device is a newly created one;
11828 * create a peer owning data this device share it with */
11829 peer.createObject();
11830 peer->init(mPeer, *it, true /* aReshare */);
11831 }
11832 else
11833 {
11834 /* remove peer from the old list */
11835 mPeer->mStorageControllers->remove(peer);
11836 }
11837 /* and add it to the new list */
11838 newList->push_back(peer);
11839
11840 ++it;
11841 }
11842
11843 /* uninit old peer's controllers that are left */
11844 it = mPeer->mStorageControllers->begin();
11845 while (it != mPeer->mStorageControllers->end())
11846 {
11847 (*it)->uninit();
11848 ++it;
11849 }
11850
11851 /* attach new list of controllers to our peer */
11852 mPeer->mStorageControllers.attach(newList);
11853 }
11854 else
11855 {
11856 /* we have no peer (our parent is the newly created machine);
11857 * just commit changes to devices */
11858 commitStorageControllers = true;
11859 }
11860 }
11861 else
11862 {
11863 /* the list of controllers itself is not changed,
11864 * just commit changes to controllers themselves */
11865 commitStorageControllers = true;
11866 }
11867
11868 if (commitStorageControllers)
11869 {
11870 StorageControllerList::const_iterator it = mStorageControllers->begin();
11871 while (it != mStorageControllers->end())
11872 {
11873 (*it)->commit();
11874 ++it;
11875 }
11876 }
11877
11878 if (isSessionMachine())
11879 {
11880 /* attach new data to the primary machine and reshare it */
11881 mPeer->mUserData.attach(mUserData);
11882 mPeer->mHWData.attach(mHWData);
11883 /* mMediaData is reshared by fixupMedia */
11884 // mPeer->mMediaData.attach(mMediaData);
11885 Assert(mPeer->mMediaData.data() == mMediaData.data());
11886 }
11887}
11888
11889/**
11890 * Copies all the hardware data from the given machine.
11891 *
11892 * Currently, only called when the VM is being restored from a snapshot. In
11893 * particular, this implies that the VM is not running during this method's
11894 * call.
11895 *
11896 * @note This method must be called from under this object's lock.
11897 *
11898 * @note This method doesn't call #commit(), so all data remains backed up and
11899 * unsaved.
11900 */
11901void Machine::copyFrom(Machine *aThat)
11902{
11903 AssertReturnVoid(!isSnapshotMachine());
11904 AssertReturnVoid(aThat->isSnapshotMachine());
11905
11906 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11907
11908 mHWData.assignCopy(aThat->mHWData);
11909
11910 // create copies of all shared folders (mHWData after attaching a copy
11911 // contains just references to original objects)
11912 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11913 it != mHWData->mSharedFolders.end();
11914 ++it)
11915 {
11916 ComObjPtr<SharedFolder> folder;
11917 folder.createObject();
11918 HRESULT rc = folder->initCopy(getMachine(), *it);
11919 AssertComRC(rc);
11920 *it = folder;
11921 }
11922
11923 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11924 mVRDEServer->copyFrom(aThat->mVRDEServer);
11925 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11926 mUSBController->copyFrom(aThat->mUSBController);
11927 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11928
11929 /* create private copies of all controllers */
11930 mStorageControllers.backup();
11931 mStorageControllers->clear();
11932 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11933 it != aThat->mStorageControllers->end();
11934 ++it)
11935 {
11936 ComObjPtr<StorageController> ctrl;
11937 ctrl.createObject();
11938 ctrl->initCopy(this, *it);
11939 mStorageControllers->push_back(ctrl);
11940 }
11941
11942 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11943 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11944 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11945 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11946 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11947 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11948 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11949}
11950
11951/**
11952 * Returns whether the given storage controller is hotplug capable.
11953 *
11954 * @returns true if the controller supports hotplugging
11955 * false otherwise.
11956 * @param enmCtrlType The controller type to check for.
11957 */
11958bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11959{
11960 switch (enmCtrlType)
11961 {
11962 case StorageControllerType_IntelAhci:
11963 return true;
11964 case StorageControllerType_LsiLogic:
11965 case StorageControllerType_LsiLogicSas:
11966 case StorageControllerType_BusLogic:
11967 case StorageControllerType_PIIX3:
11968 case StorageControllerType_PIIX4:
11969 case StorageControllerType_ICH6:
11970 case StorageControllerType_I82078:
11971 default:
11972 return false;
11973 }
11974}
11975
11976#ifdef VBOX_WITH_RESOURCE_USAGE_API
11977
11978void Machine::getDiskList(MediaList &list)
11979{
11980 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11981 it != mMediaData->mAttachments.end();
11982 ++it)
11983 {
11984 MediumAttachment* pAttach = *it;
11985 /* just in case */
11986 AssertStmt(pAttach, continue);
11987
11988 AutoCaller localAutoCallerA(pAttach);
11989 if (FAILED(localAutoCallerA.rc())) continue;
11990
11991 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11992
11993 if (pAttach->getType() == DeviceType_HardDisk)
11994 list.push_back(pAttach->getMedium());
11995 }
11996}
11997
11998void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11999{
12000 AssertReturnVoid(isWriteLockOnCurrentThread());
12001 AssertPtrReturnVoid(aCollector);
12002
12003 pm::CollectorHAL *hal = aCollector->getHAL();
12004 /* Create sub metrics */
12005 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12006 "Percentage of processor time spent in user mode by the VM process.");
12007 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12008 "Percentage of processor time spent in kernel mode by the VM process.");
12009 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12010 "Size of resident portion of VM process in memory.");
12011 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12012 "Actual size of all VM disks combined.");
12013 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12014 "Network receive rate.");
12015 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12016 "Network transmit rate.");
12017 /* Create and register base metrics */
12018 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12019 cpuLoadUser, cpuLoadKernel);
12020 aCollector->registerBaseMetric(cpuLoad);
12021 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12022 ramUsageUsed);
12023 aCollector->registerBaseMetric(ramUsage);
12024 MediaList disks;
12025 getDiskList(disks);
12026 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12027 diskUsageUsed);
12028 aCollector->registerBaseMetric(diskUsage);
12029
12030 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12031 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12032 new pm::AggregateAvg()));
12033 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12034 new pm::AggregateMin()));
12035 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12036 new pm::AggregateMax()));
12037 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12038 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12039 new pm::AggregateAvg()));
12040 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12041 new pm::AggregateMin()));
12042 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12043 new pm::AggregateMax()));
12044
12045 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12046 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12047 new pm::AggregateAvg()));
12048 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12049 new pm::AggregateMin()));
12050 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12051 new pm::AggregateMax()));
12052
12053 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12054 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12055 new pm::AggregateAvg()));
12056 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12057 new pm::AggregateMin()));
12058 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12059 new pm::AggregateMax()));
12060
12061
12062 /* Guest metrics collector */
12063 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12064 aCollector->registerGuest(mCollectorGuest);
12065 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12066 this, __PRETTY_FUNCTION__, mCollectorGuest));
12067
12068 /* Create sub metrics */
12069 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12070 "Percentage of processor time spent in user mode as seen by the guest.");
12071 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12072 "Percentage of processor time spent in kernel mode as seen by the guest.");
12073 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12074 "Percentage of processor time spent idling as seen by the guest.");
12075
12076 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12077 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12078 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12079 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12080 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12081 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12082
12083 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12084
12085 /* Create and register base metrics */
12086 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12087 machineNetRx, machineNetTx);
12088 aCollector->registerBaseMetric(machineNetRate);
12089
12090 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12091 guestLoadUser, guestLoadKernel, guestLoadIdle);
12092 aCollector->registerBaseMetric(guestCpuLoad);
12093
12094 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12095 guestMemTotal, guestMemFree,
12096 guestMemBalloon, guestMemShared,
12097 guestMemCache, guestPagedTotal);
12098 aCollector->registerBaseMetric(guestCpuMem);
12099
12100 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12101 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12102 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12103 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12104
12105 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12106 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12107 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12108 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12109
12110 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12111 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12112 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12113 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12114
12115 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12116 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12117 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12118 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12119
12120 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12121 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12122 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12123 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12124
12125 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12126 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12127 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12128 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12129
12130 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12131 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12132 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12133 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12134
12135 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12136 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12137 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12138 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12139
12140 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12141 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12142 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12143 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12144
12145 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12146 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12147 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12148 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12149
12150 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12151 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12152 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12153 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12154}
12155
12156void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12157{
12158 AssertReturnVoid(isWriteLockOnCurrentThread());
12159
12160 if (aCollector)
12161 {
12162 aCollector->unregisterMetricsFor(aMachine);
12163 aCollector->unregisterBaseMetricsFor(aMachine);
12164 }
12165}
12166
12167#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12168
12169
12170////////////////////////////////////////////////////////////////////////////////
12171
12172DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12173
12174HRESULT SessionMachine::FinalConstruct()
12175{
12176 LogFlowThisFunc(("\n"));
12177
12178#if defined(RT_OS_WINDOWS)
12179 mIPCSem = NULL;
12180#elif defined(RT_OS_OS2)
12181 mIPCSem = NULLHANDLE;
12182#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12183 mIPCSem = -1;
12184#else
12185# error "Port me!"
12186#endif
12187
12188 return BaseFinalConstruct();
12189}
12190
12191void SessionMachine::FinalRelease()
12192{
12193 LogFlowThisFunc(("\n"));
12194
12195 uninit(Uninit::Unexpected);
12196
12197 BaseFinalRelease();
12198}
12199
12200/**
12201 * @note Must be called only by Machine::openSession() from its own write lock.
12202 */
12203HRESULT SessionMachine::init(Machine *aMachine)
12204{
12205 LogFlowThisFuncEnter();
12206 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12207
12208 AssertReturn(aMachine, E_INVALIDARG);
12209
12210 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12211
12212 /* Enclose the state transition NotReady->InInit->Ready */
12213 AutoInitSpan autoInitSpan(this);
12214 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12215
12216 /* create the interprocess semaphore */
12217#if defined(RT_OS_WINDOWS)
12218 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12219 for (size_t i = 0; i < mIPCSemName.length(); i++)
12220 if (mIPCSemName.raw()[i] == '\\')
12221 mIPCSemName.raw()[i] = '/';
12222 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12223 ComAssertMsgRet(mIPCSem,
12224 ("Cannot create IPC mutex '%ls', err=%d",
12225 mIPCSemName.raw(), ::GetLastError()),
12226 E_FAIL);
12227#elif defined(RT_OS_OS2)
12228 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12229 aMachine->mData->mUuid.raw());
12230 mIPCSemName = ipcSem;
12231 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12232 ComAssertMsgRet(arc == NO_ERROR,
12233 ("Cannot create IPC mutex '%s', arc=%ld",
12234 ipcSem.c_str(), arc),
12235 E_FAIL);
12236#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12237# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12238# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12239 /** @todo Check that this still works correctly. */
12240 AssertCompileSize(key_t, 8);
12241# else
12242 AssertCompileSize(key_t, 4);
12243# endif
12244 key_t key;
12245 mIPCSem = -1;
12246 mIPCKey = "0";
12247 for (uint32_t i = 0; i < 1 << 24; i++)
12248 {
12249 key = ((uint32_t)'V' << 24) | i;
12250 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12251 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12252 {
12253 mIPCSem = sem;
12254 if (sem >= 0)
12255 mIPCKey = BstrFmt("%u", key);
12256 break;
12257 }
12258 }
12259# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12260 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12261 char *pszSemName = NULL;
12262 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12263 key_t key = ::ftok(pszSemName, 'V');
12264 RTStrFree(pszSemName);
12265
12266 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12267# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12268
12269 int errnoSave = errno;
12270 if (mIPCSem < 0 && errnoSave == ENOSYS)
12271 {
12272 setError(E_FAIL,
12273 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12274 "support for SysV IPC. Check the host kernel configuration for "
12275 "CONFIG_SYSVIPC=y"));
12276 return E_FAIL;
12277 }
12278 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12279 * the IPC semaphores */
12280 if (mIPCSem < 0 && errnoSave == ENOSPC)
12281 {
12282#ifdef RT_OS_LINUX
12283 setError(E_FAIL,
12284 tr("Cannot create IPC semaphore because the system limit for the "
12285 "maximum number of semaphore sets (SEMMNI), or the system wide "
12286 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12287 "current set of SysV IPC semaphores can be determined from "
12288 "the file /proc/sysvipc/sem"));
12289#else
12290 setError(E_FAIL,
12291 tr("Cannot create IPC semaphore because the system-imposed limit "
12292 "on the maximum number of allowed semaphores or semaphore "
12293 "identifiers system-wide would be exceeded"));
12294#endif
12295 return E_FAIL;
12296 }
12297 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12298 E_FAIL);
12299 /* set the initial value to 1 */
12300 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12301 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12302 E_FAIL);
12303#else
12304# error "Port me!"
12305#endif
12306
12307 /* memorize the peer Machine */
12308 unconst(mPeer) = aMachine;
12309 /* share the parent pointer */
12310 unconst(mParent) = aMachine->mParent;
12311
12312 /* take the pointers to data to share */
12313 mData.share(aMachine->mData);
12314 mSSData.share(aMachine->mSSData);
12315
12316 mUserData.share(aMachine->mUserData);
12317 mHWData.share(aMachine->mHWData);
12318 mMediaData.share(aMachine->mMediaData);
12319
12320 mStorageControllers.allocate();
12321 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12322 it != aMachine->mStorageControllers->end();
12323 ++it)
12324 {
12325 ComObjPtr<StorageController> ctl;
12326 ctl.createObject();
12327 ctl->init(this, *it);
12328 mStorageControllers->push_back(ctl);
12329 }
12330
12331 unconst(mBIOSSettings).createObject();
12332 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12333 /* create another VRDEServer object that will be mutable */
12334 unconst(mVRDEServer).createObject();
12335 mVRDEServer->init(this, aMachine->mVRDEServer);
12336 /* create another audio adapter object that will be mutable */
12337 unconst(mAudioAdapter).createObject();
12338 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12339 /* create a list of serial ports that will be mutable */
12340 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12341 {
12342 unconst(mSerialPorts[slot]).createObject();
12343 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12344 }
12345 /* create a list of parallel ports that will be mutable */
12346 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12347 {
12348 unconst(mParallelPorts[slot]).createObject();
12349 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12350 }
12351 /* create another USB controller object that will be mutable */
12352 unconst(mUSBController).createObject();
12353 mUSBController->init(this, aMachine->mUSBController);
12354
12355 /* create a list of network adapters that will be mutable */
12356 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12357 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12358 {
12359 unconst(mNetworkAdapters[slot]).createObject();
12360 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12361 }
12362
12363 /* create another bandwidth control object that will be mutable */
12364 unconst(mBandwidthControl).createObject();
12365 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12366
12367 /* default is to delete saved state on Saved -> PoweredOff transition */
12368 mRemoveSavedState = true;
12369
12370 /* Confirm a successful initialization when it's the case */
12371 autoInitSpan.setSucceeded();
12372
12373 LogFlowThisFuncLeave();
12374 return S_OK;
12375}
12376
12377/**
12378 * Uninitializes this session object. If the reason is other than
12379 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12380 *
12381 * @param aReason uninitialization reason
12382 *
12383 * @note Locks mParent + this object for writing.
12384 */
12385void SessionMachine::uninit(Uninit::Reason aReason)
12386{
12387 LogFlowThisFuncEnter();
12388 LogFlowThisFunc(("reason=%d\n", aReason));
12389
12390 /*
12391 * Strongly reference ourselves to prevent this object deletion after
12392 * mData->mSession.mMachine.setNull() below (which can release the last
12393 * reference and call the destructor). Important: this must be done before
12394 * accessing any members (and before AutoUninitSpan that does it as well).
12395 * This self reference will be released as the very last step on return.
12396 */
12397 ComObjPtr<SessionMachine> selfRef = this;
12398
12399 /* Enclose the state transition Ready->InUninit->NotReady */
12400 AutoUninitSpan autoUninitSpan(this);
12401 if (autoUninitSpan.uninitDone())
12402 {
12403 LogFlowThisFunc(("Already uninitialized\n"));
12404 LogFlowThisFuncLeave();
12405 return;
12406 }
12407
12408 if (autoUninitSpan.initFailed())
12409 {
12410 /* We've been called by init() because it's failed. It's not really
12411 * necessary (nor it's safe) to perform the regular uninit sequence
12412 * below, the following is enough.
12413 */
12414 LogFlowThisFunc(("Initialization failed.\n"));
12415#if defined(RT_OS_WINDOWS)
12416 if (mIPCSem)
12417 ::CloseHandle(mIPCSem);
12418 mIPCSem = NULL;
12419#elif defined(RT_OS_OS2)
12420 if (mIPCSem != NULLHANDLE)
12421 ::DosCloseMutexSem(mIPCSem);
12422 mIPCSem = NULLHANDLE;
12423#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12424 if (mIPCSem >= 0)
12425 ::semctl(mIPCSem, 0, IPC_RMID);
12426 mIPCSem = -1;
12427# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12428 mIPCKey = "0";
12429# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12430#else
12431# error "Port me!"
12432#endif
12433 uninitDataAndChildObjects();
12434 mData.free();
12435 unconst(mParent) = NULL;
12436 unconst(mPeer) = NULL;
12437 LogFlowThisFuncLeave();
12438 return;
12439 }
12440
12441 MachineState_T lastState;
12442 {
12443 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12444 lastState = mData->mMachineState;
12445 }
12446 NOREF(lastState);
12447
12448#ifdef VBOX_WITH_USB
12449 // release all captured USB devices, but do this before requesting the locks below
12450 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12451 {
12452 /* Console::captureUSBDevices() is called in the VM process only after
12453 * setting the machine state to Starting or Restoring.
12454 * Console::detachAllUSBDevices() will be called upon successful
12455 * termination. So, we need to release USB devices only if there was
12456 * an abnormal termination of a running VM.
12457 *
12458 * This is identical to SessionMachine::DetachAllUSBDevices except
12459 * for the aAbnormal argument. */
12460 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12461 AssertComRC(rc);
12462 NOREF(rc);
12463
12464 USBProxyService *service = mParent->host()->usbProxyService();
12465 if (service)
12466 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12467 }
12468#endif /* VBOX_WITH_USB */
12469
12470 // we need to lock this object in uninit() because the lock is shared
12471 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12472 // and others need mParent lock, and USB needs host lock.
12473 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12474
12475#if 0
12476 // Trigger async cleanup tasks, avoid doing things here which are not
12477 // vital to be done immediately and maybe need more locks. This calls
12478 // Machine::unregisterMetrics().
12479 mParent->onMachineUninit(mPeer);
12480#else
12481 /*
12482 * It is safe to call Machine::unregisterMetrics() here because
12483 * PerformanceCollector::samplerCallback no longer accesses guest methods
12484 * holding the lock.
12485 */
12486 unregisterMetrics(mParent->performanceCollector(), mPeer);
12487#endif
12488 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12489 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12490 this, __PRETTY_FUNCTION__, mCollectorGuest));
12491 if (mCollectorGuest)
12492 {
12493 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12494 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12495 mCollectorGuest = NULL;
12496 }
12497
12498 if (aReason == Uninit::Abnormal)
12499 {
12500 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12501 Global::IsOnlineOrTransient(lastState)));
12502
12503 /* reset the state to Aborted */
12504 if (mData->mMachineState != MachineState_Aborted)
12505 setMachineState(MachineState_Aborted);
12506 }
12507
12508 // any machine settings modified?
12509 if (mData->flModifications)
12510 {
12511 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12512 rollback(false /* aNotify */);
12513 }
12514
12515 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12516 || !mConsoleTaskData.mSnapshot);
12517 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12518 {
12519 LogWarningThisFunc(("canceling failed save state request!\n"));
12520 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12521 }
12522 else if (!mConsoleTaskData.mSnapshot.isNull())
12523 {
12524 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12525
12526 /* delete all differencing hard disks created (this will also attach
12527 * their parents back by rolling back mMediaData) */
12528 rollbackMedia();
12529
12530 // delete the saved state file (it might have been already created)
12531 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12532 // think it's still in use
12533 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12534 mConsoleTaskData.mSnapshot->uninit();
12535 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12536 }
12537
12538 if (!mData->mSession.mType.isEmpty())
12539 {
12540 /* mType is not null when this machine's process has been started by
12541 * Machine::LaunchVMProcess(), therefore it is our child. We
12542 * need to queue the PID to reap the process (and avoid zombies on
12543 * Linux). */
12544 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12545 mParent->addProcessToReap(mData->mSession.mPID);
12546 }
12547
12548 mData->mSession.mPID = NIL_RTPROCESS;
12549
12550 if (aReason == Uninit::Unexpected)
12551 {
12552 /* Uninitialization didn't come from #checkForDeath(), so tell the
12553 * client watcher thread to update the set of machines that have open
12554 * sessions. */
12555 mParent->updateClientWatcher();
12556 }
12557
12558 /* uninitialize all remote controls */
12559 if (mData->mSession.mRemoteControls.size())
12560 {
12561 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12562 mData->mSession.mRemoteControls.size()));
12563
12564 Data::Session::RemoteControlList::iterator it =
12565 mData->mSession.mRemoteControls.begin();
12566 while (it != mData->mSession.mRemoteControls.end())
12567 {
12568 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12569 HRESULT rc = (*it)->Uninitialize();
12570 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12571 if (FAILED(rc))
12572 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12573 ++it;
12574 }
12575 mData->mSession.mRemoteControls.clear();
12576 }
12577
12578 /*
12579 * An expected uninitialization can come only from #checkForDeath().
12580 * Otherwise it means that something's gone really wrong (for example,
12581 * the Session implementation has released the VirtualBox reference
12582 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12583 * etc). However, it's also possible, that the client releases the IPC
12584 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12585 * but the VirtualBox release event comes first to the server process.
12586 * This case is practically possible, so we should not assert on an
12587 * unexpected uninit, just log a warning.
12588 */
12589
12590 if ((aReason == Uninit::Unexpected))
12591 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12592
12593 if (aReason != Uninit::Normal)
12594 {
12595 mData->mSession.mDirectControl.setNull();
12596 }
12597 else
12598 {
12599 /* this must be null here (see #OnSessionEnd()) */
12600 Assert(mData->mSession.mDirectControl.isNull());
12601 Assert(mData->mSession.mState == SessionState_Unlocking);
12602 Assert(!mData->mSession.mProgress.isNull());
12603 }
12604 if (mData->mSession.mProgress)
12605 {
12606 if (aReason == Uninit::Normal)
12607 mData->mSession.mProgress->notifyComplete(S_OK);
12608 else
12609 mData->mSession.mProgress->notifyComplete(E_FAIL,
12610 COM_IIDOF(ISession),
12611 getComponentName(),
12612 tr("The VM session was aborted"));
12613 mData->mSession.mProgress.setNull();
12614 }
12615
12616 /* remove the association between the peer machine and this session machine */
12617 Assert( (SessionMachine*)mData->mSession.mMachine == this
12618 || aReason == Uninit::Unexpected);
12619
12620 /* reset the rest of session data */
12621 mData->mSession.mMachine.setNull();
12622 mData->mSession.mState = SessionState_Unlocked;
12623 mData->mSession.mType.setNull();
12624
12625 /* close the interprocess semaphore before leaving the exclusive lock */
12626#if defined(RT_OS_WINDOWS)
12627 if (mIPCSem)
12628 ::CloseHandle(mIPCSem);
12629 mIPCSem = NULL;
12630#elif defined(RT_OS_OS2)
12631 if (mIPCSem != NULLHANDLE)
12632 ::DosCloseMutexSem(mIPCSem);
12633 mIPCSem = NULLHANDLE;
12634#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12635 if (mIPCSem >= 0)
12636 ::semctl(mIPCSem, 0, IPC_RMID);
12637 mIPCSem = -1;
12638# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12639 mIPCKey = "0";
12640# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12641#else
12642# error "Port me!"
12643#endif
12644
12645 /* fire an event */
12646 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12647
12648 uninitDataAndChildObjects();
12649
12650 /* free the essential data structure last */
12651 mData.free();
12652
12653 /* release the exclusive lock before setting the below two to NULL */
12654 multilock.release();
12655
12656 unconst(mParent) = NULL;
12657 unconst(mPeer) = NULL;
12658
12659 LogFlowThisFuncLeave();
12660}
12661
12662// util::Lockable interface
12663////////////////////////////////////////////////////////////////////////////////
12664
12665/**
12666 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12667 * with the primary Machine instance (mPeer).
12668 */
12669RWLockHandle *SessionMachine::lockHandle() const
12670{
12671 AssertReturn(mPeer != NULL, NULL);
12672 return mPeer->lockHandle();
12673}
12674
12675// IInternalMachineControl methods
12676////////////////////////////////////////////////////////////////////////////////
12677
12678/**
12679 * Passes collected guest statistics to performance collector object
12680 */
12681STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12682 ULONG aCpuKernel, ULONG aCpuIdle,
12683 ULONG aMemTotal, ULONG aMemFree,
12684 ULONG aMemBalloon, ULONG aMemShared,
12685 ULONG aMemCache, ULONG aPageTotal,
12686 ULONG aAllocVMM, ULONG aFreeVMM,
12687 ULONG aBalloonedVMM, ULONG aSharedVMM,
12688 ULONG aVmNetRx, ULONG aVmNetTx)
12689{
12690 if (mCollectorGuest)
12691 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12692 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12693 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12694 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12695
12696 return S_OK;
12697}
12698
12699/**
12700 * @note Locks this object for writing.
12701 */
12702STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12703{
12704 AutoCaller autoCaller(this);
12705 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12706
12707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12708
12709 mRemoveSavedState = aRemove;
12710
12711 return S_OK;
12712}
12713
12714/**
12715 * @note Locks the same as #setMachineState() does.
12716 */
12717STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12718{
12719 return setMachineState(aMachineState);
12720}
12721
12722/**
12723 * @note Locks this object for reading.
12724 */
12725STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12726{
12727 AutoCaller autoCaller(this);
12728 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12729
12730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12731
12732#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12733 mIPCSemName.cloneTo(aId);
12734 return S_OK;
12735#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12736# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12737 mIPCKey.cloneTo(aId);
12738# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12739 mData->m_strConfigFileFull.cloneTo(aId);
12740# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12741 return S_OK;
12742#else
12743# error "Port me!"
12744#endif
12745}
12746
12747/**
12748 * @note Locks this object for writing.
12749 */
12750STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12751{
12752 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12753 AutoCaller autoCaller(this);
12754 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12755
12756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12757
12758 if (mData->mSession.mState != SessionState_Locked)
12759 return VBOX_E_INVALID_OBJECT_STATE;
12760
12761 if (!mData->mSession.mProgress.isNull())
12762 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12763
12764 LogFlowThisFunc(("returns S_OK.\n"));
12765 return S_OK;
12766}
12767
12768/**
12769 * @note Locks this object for writing.
12770 */
12771STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12772{
12773 AutoCaller autoCaller(this);
12774 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12775
12776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12777
12778 if (mData->mSession.mState != SessionState_Locked)
12779 return VBOX_E_INVALID_OBJECT_STATE;
12780
12781 /* Finalize the LaunchVMProcess progress object. */
12782 if (mData->mSession.mProgress)
12783 {
12784 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12785 mData->mSession.mProgress.setNull();
12786 }
12787
12788 if (SUCCEEDED((HRESULT)iResult))
12789 {
12790#ifdef VBOX_WITH_RESOURCE_USAGE_API
12791 /* The VM has been powered up successfully, so it makes sense
12792 * now to offer the performance metrics for a running machine
12793 * object. Doing it earlier wouldn't be safe. */
12794 registerMetrics(mParent->performanceCollector(), mPeer,
12795 mData->mSession.mPID);
12796#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12797 }
12798
12799 return S_OK;
12800}
12801
12802/**
12803 * @note Locks this object for writing.
12804 */
12805STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12806{
12807 LogFlowThisFuncEnter();
12808
12809 CheckComArgOutPointerValid(aProgress);
12810
12811 AutoCaller autoCaller(this);
12812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12813
12814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12815
12816 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12817 E_FAIL);
12818
12819 /* create a progress object to track operation completion */
12820 ComObjPtr<Progress> pProgress;
12821 pProgress.createObject();
12822 pProgress->init(getVirtualBox(),
12823 static_cast<IMachine *>(this) /* aInitiator */,
12824 Bstr(tr("Stopping the virtual machine")).raw(),
12825 FALSE /* aCancelable */);
12826
12827 /* fill in the console task data */
12828 mConsoleTaskData.mLastState = mData->mMachineState;
12829 mConsoleTaskData.mProgress = pProgress;
12830
12831 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12832 setMachineState(MachineState_Stopping);
12833
12834 pProgress.queryInterfaceTo(aProgress);
12835
12836 return S_OK;
12837}
12838
12839/**
12840 * @note Locks this object for writing.
12841 */
12842STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12843{
12844 LogFlowThisFuncEnter();
12845
12846 AutoCaller autoCaller(this);
12847 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12848
12849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12850
12851 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12852 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12853 && mConsoleTaskData.mLastState != MachineState_Null,
12854 E_FAIL);
12855
12856 /*
12857 * On failure, set the state to the state we had when BeginPoweringDown()
12858 * was called (this is expected by Console::PowerDown() and the associated
12859 * task). On success the VM process already changed the state to
12860 * MachineState_PoweredOff, so no need to do anything.
12861 */
12862 if (FAILED(iResult))
12863 setMachineState(mConsoleTaskData.mLastState);
12864
12865 /* notify the progress object about operation completion */
12866 Assert(mConsoleTaskData.mProgress);
12867 if (SUCCEEDED(iResult))
12868 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12869 else
12870 {
12871 Utf8Str strErrMsg(aErrMsg);
12872 if (strErrMsg.length())
12873 mConsoleTaskData.mProgress->notifyComplete(iResult,
12874 COM_IIDOF(ISession),
12875 getComponentName(),
12876 strErrMsg.c_str());
12877 else
12878 mConsoleTaskData.mProgress->notifyComplete(iResult);
12879 }
12880
12881 /* clear out the temporary saved state data */
12882 mConsoleTaskData.mLastState = MachineState_Null;
12883 mConsoleTaskData.mProgress.setNull();
12884
12885 LogFlowThisFuncLeave();
12886 return S_OK;
12887}
12888
12889
12890/**
12891 * Goes through the USB filters of the given machine to see if the given
12892 * device matches any filter or not.
12893 *
12894 * @note Locks the same as USBController::hasMatchingFilter() does.
12895 */
12896STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12897 BOOL *aMatched,
12898 ULONG *aMaskedIfs)
12899{
12900 LogFlowThisFunc(("\n"));
12901
12902 CheckComArgNotNull(aUSBDevice);
12903 CheckComArgOutPointerValid(aMatched);
12904
12905 AutoCaller autoCaller(this);
12906 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12907
12908#ifdef VBOX_WITH_USB
12909 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12910#else
12911 NOREF(aUSBDevice);
12912 NOREF(aMaskedIfs);
12913 *aMatched = FALSE;
12914#endif
12915
12916 return S_OK;
12917}
12918
12919/**
12920 * @note Locks the same as Host::captureUSBDevice() does.
12921 */
12922STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12923{
12924 LogFlowThisFunc(("\n"));
12925
12926 AutoCaller autoCaller(this);
12927 AssertComRCReturnRC(autoCaller.rc());
12928
12929#ifdef VBOX_WITH_USB
12930 /* if captureDeviceForVM() fails, it must have set extended error info */
12931 clearError();
12932 MultiResult rc = mParent->host()->checkUSBProxyService();
12933 if (FAILED(rc)) return rc;
12934
12935 USBProxyService *service = mParent->host()->usbProxyService();
12936 AssertReturn(service, E_FAIL);
12937 return service->captureDeviceForVM(this, Guid(aId).ref());
12938#else
12939 NOREF(aId);
12940 return E_NOTIMPL;
12941#endif
12942}
12943
12944/**
12945 * @note Locks the same as Host::detachUSBDevice() does.
12946 */
12947STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12948{
12949 LogFlowThisFunc(("\n"));
12950
12951 AutoCaller autoCaller(this);
12952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12953
12954#ifdef VBOX_WITH_USB
12955 USBProxyService *service = mParent->host()->usbProxyService();
12956 AssertReturn(service, E_FAIL);
12957 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12958#else
12959 NOREF(aId);
12960 NOREF(aDone);
12961 return E_NOTIMPL;
12962#endif
12963}
12964
12965/**
12966 * Inserts all machine filters to the USB proxy service and then calls
12967 * Host::autoCaptureUSBDevices().
12968 *
12969 * Called by Console from the VM process upon VM startup.
12970 *
12971 * @note Locks what called methods lock.
12972 */
12973STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12974{
12975 LogFlowThisFunc(("\n"));
12976
12977 AutoCaller autoCaller(this);
12978 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12979
12980#ifdef VBOX_WITH_USB
12981 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12982 AssertComRC(rc);
12983 NOREF(rc);
12984
12985 USBProxyService *service = mParent->host()->usbProxyService();
12986 AssertReturn(service, E_FAIL);
12987 return service->autoCaptureDevicesForVM(this);
12988#else
12989 return S_OK;
12990#endif
12991}
12992
12993/**
12994 * Removes all machine filters from the USB proxy service and then calls
12995 * Host::detachAllUSBDevices().
12996 *
12997 * Called by Console from the VM process upon normal VM termination or by
12998 * SessionMachine::uninit() upon abnormal VM termination (from under the
12999 * Machine/SessionMachine lock).
13000 *
13001 * @note Locks what called methods lock.
13002 */
13003STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13004{
13005 LogFlowThisFunc(("\n"));
13006
13007 AutoCaller autoCaller(this);
13008 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13009
13010#ifdef VBOX_WITH_USB
13011 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13012 AssertComRC(rc);
13013 NOREF(rc);
13014
13015 USBProxyService *service = mParent->host()->usbProxyService();
13016 AssertReturn(service, E_FAIL);
13017 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13018#else
13019 NOREF(aDone);
13020 return S_OK;
13021#endif
13022}
13023
13024/**
13025 * @note Locks this object for writing.
13026 */
13027STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13028 IProgress **aProgress)
13029{
13030 LogFlowThisFuncEnter();
13031
13032 AssertReturn(aSession, E_INVALIDARG);
13033 AssertReturn(aProgress, E_INVALIDARG);
13034
13035 AutoCaller autoCaller(this);
13036
13037 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13038 /*
13039 * We don't assert below because it might happen that a non-direct session
13040 * informs us it is closed right after we've been uninitialized -- it's ok.
13041 */
13042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13043
13044 /* get IInternalSessionControl interface */
13045 ComPtr<IInternalSessionControl> control(aSession);
13046
13047 ComAssertRet(!control.isNull(), E_INVALIDARG);
13048
13049 /* Creating a Progress object requires the VirtualBox lock, and
13050 * thus locking it here is required by the lock order rules. */
13051 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13052
13053 if (control == mData->mSession.mDirectControl)
13054 {
13055 ComAssertRet(aProgress, E_POINTER);
13056
13057 /* The direct session is being normally closed by the client process
13058 * ----------------------------------------------------------------- */
13059
13060 /* go to the closing state (essential for all open*Session() calls and
13061 * for #checkForDeath()) */
13062 Assert(mData->mSession.mState == SessionState_Locked);
13063 mData->mSession.mState = SessionState_Unlocking;
13064
13065 /* set direct control to NULL to release the remote instance */
13066 mData->mSession.mDirectControl.setNull();
13067 LogFlowThisFunc(("Direct control is set to NULL\n"));
13068
13069 if (mData->mSession.mProgress)
13070 {
13071 /* finalize the progress, someone might wait if a frontend
13072 * closes the session before powering on the VM. */
13073 mData->mSession.mProgress->notifyComplete(E_FAIL,
13074 COM_IIDOF(ISession),
13075 getComponentName(),
13076 tr("The VM session was closed before any attempt to power it on"));
13077 mData->mSession.mProgress.setNull();
13078 }
13079
13080 /* Create the progress object the client will use to wait until
13081 * #checkForDeath() is called to uninitialize this session object after
13082 * it releases the IPC semaphore.
13083 * Note! Because we're "reusing" mProgress here, this must be a proxy
13084 * object just like for LaunchVMProcess. */
13085 Assert(mData->mSession.mProgress.isNull());
13086 ComObjPtr<ProgressProxy> progress;
13087 progress.createObject();
13088 ComPtr<IUnknown> pPeer(mPeer);
13089 progress->init(mParent, pPeer,
13090 Bstr(tr("Closing session")).raw(),
13091 FALSE /* aCancelable */);
13092 progress.queryInterfaceTo(aProgress);
13093 mData->mSession.mProgress = progress;
13094 }
13095 else
13096 {
13097 /* the remote session is being normally closed */
13098 Data::Session::RemoteControlList::iterator it =
13099 mData->mSession.mRemoteControls.begin();
13100 while (it != mData->mSession.mRemoteControls.end())
13101 {
13102 if (control == *it)
13103 break;
13104 ++it;
13105 }
13106 BOOL found = it != mData->mSession.mRemoteControls.end();
13107 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13108 E_INVALIDARG);
13109 // This MUST be erase(it), not remove(*it) as the latter triggers a
13110 // very nasty use after free due to the place where the value "lives".
13111 mData->mSession.mRemoteControls.erase(it);
13112 }
13113
13114 /* signal the client watcher thread, because the client is going away */
13115 mParent->updateClientWatcher();
13116
13117 LogFlowThisFuncLeave();
13118 return S_OK;
13119}
13120
13121/**
13122 * @note Locks this object for writing.
13123 */
13124STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13125{
13126 LogFlowThisFuncEnter();
13127
13128 CheckComArgOutPointerValid(aProgress);
13129 CheckComArgOutPointerValid(aStateFilePath);
13130
13131 AutoCaller autoCaller(this);
13132 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13133
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 AssertReturn( mData->mMachineState == MachineState_Paused
13137 && mConsoleTaskData.mLastState == MachineState_Null
13138 && mConsoleTaskData.strStateFilePath.isEmpty(),
13139 E_FAIL);
13140
13141 /* create a progress object to track operation completion */
13142 ComObjPtr<Progress> pProgress;
13143 pProgress.createObject();
13144 pProgress->init(getVirtualBox(),
13145 static_cast<IMachine *>(this) /* aInitiator */,
13146 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13147 FALSE /* aCancelable */);
13148
13149 Utf8Str strStateFilePath;
13150 /* stateFilePath is null when the machine is not running */
13151 if (mData->mMachineState == MachineState_Paused)
13152 composeSavedStateFilename(strStateFilePath);
13153
13154 /* fill in the console task data */
13155 mConsoleTaskData.mLastState = mData->mMachineState;
13156 mConsoleTaskData.strStateFilePath = strStateFilePath;
13157 mConsoleTaskData.mProgress = pProgress;
13158
13159 /* set the state to Saving (this is expected by Console::SaveState()) */
13160 setMachineState(MachineState_Saving);
13161
13162 strStateFilePath.cloneTo(aStateFilePath);
13163 pProgress.queryInterfaceTo(aProgress);
13164
13165 return S_OK;
13166}
13167
13168/**
13169 * @note Locks mParent + this object for writing.
13170 */
13171STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13172{
13173 LogFlowThisFunc(("\n"));
13174
13175 AutoCaller autoCaller(this);
13176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13177
13178 /* endSavingState() need mParent lock */
13179 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13180
13181 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13182 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13183 && mConsoleTaskData.mLastState != MachineState_Null
13184 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13185 E_FAIL);
13186
13187 /*
13188 * On failure, set the state to the state we had when BeginSavingState()
13189 * was called (this is expected by Console::SaveState() and the associated
13190 * task). On success the VM process already changed the state to
13191 * MachineState_Saved, so no need to do anything.
13192 */
13193 if (FAILED(iResult))
13194 setMachineState(mConsoleTaskData.mLastState);
13195
13196 return endSavingState(iResult, aErrMsg);
13197}
13198
13199/**
13200 * @note Locks this object for writing.
13201 */
13202STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13203{
13204 LogFlowThisFunc(("\n"));
13205
13206 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13207
13208 AutoCaller autoCaller(this);
13209 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13210
13211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13212
13213 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13214 || mData->mMachineState == MachineState_Teleported
13215 || mData->mMachineState == MachineState_Aborted
13216 , E_FAIL); /** @todo setError. */
13217
13218 Utf8Str stateFilePathFull = aSavedStateFile;
13219 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13220 if (RT_FAILURE(vrc))
13221 return setError(VBOX_E_FILE_ERROR,
13222 tr("Invalid saved state file path '%ls' (%Rrc)"),
13223 aSavedStateFile,
13224 vrc);
13225
13226 mSSData->strStateFilePath = stateFilePathFull;
13227
13228 /* The below setMachineState() will detect the state transition and will
13229 * update the settings file */
13230
13231 return setMachineState(MachineState_Saved);
13232}
13233
13234STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13235 ComSafeArrayOut(BSTR, aValues),
13236 ComSafeArrayOut(LONG64, aTimestamps),
13237 ComSafeArrayOut(BSTR, aFlags))
13238{
13239 LogFlowThisFunc(("\n"));
13240
13241#ifdef VBOX_WITH_GUEST_PROPS
13242 using namespace guestProp;
13243
13244 AutoCaller autoCaller(this);
13245 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13246
13247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13248
13249 CheckComArgOutSafeArrayPointerValid(aNames);
13250 CheckComArgOutSafeArrayPointerValid(aValues);
13251 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13252 CheckComArgOutSafeArrayPointerValid(aFlags);
13253
13254 size_t cEntries = mHWData->mGuestProperties.size();
13255 com::SafeArray<BSTR> names(cEntries);
13256 com::SafeArray<BSTR> values(cEntries);
13257 com::SafeArray<LONG64> timestamps(cEntries);
13258 com::SafeArray<BSTR> flags(cEntries);
13259 unsigned i = 0;
13260 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13261 it != mHWData->mGuestProperties.end();
13262 ++it)
13263 {
13264 char szFlags[MAX_FLAGS_LEN + 1];
13265 it->first.cloneTo(&names[i]);
13266 it->second.strValue.cloneTo(&values[i]);
13267 timestamps[i] = it->second.mTimestamp;
13268 /* If it is NULL, keep it NULL. */
13269 if (it->second.mFlags)
13270 {
13271 writeFlags(it->second.mFlags, szFlags);
13272 Bstr(szFlags).cloneTo(&flags[i]);
13273 }
13274 else
13275 flags[i] = NULL;
13276 ++i;
13277 }
13278 names.detachTo(ComSafeArrayOutArg(aNames));
13279 values.detachTo(ComSafeArrayOutArg(aValues));
13280 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13281 flags.detachTo(ComSafeArrayOutArg(aFlags));
13282 return S_OK;
13283#else
13284 ReturnComNotImplemented();
13285#endif
13286}
13287
13288STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13289 IN_BSTR aValue,
13290 LONG64 aTimestamp,
13291 IN_BSTR aFlags)
13292{
13293 LogFlowThisFunc(("\n"));
13294
13295#ifdef VBOX_WITH_GUEST_PROPS
13296 using namespace guestProp;
13297
13298 CheckComArgStrNotEmptyOrNull(aName);
13299 CheckComArgNotNull(aValue);
13300 CheckComArgNotNull(aFlags);
13301
13302 try
13303 {
13304 /*
13305 * Convert input up front.
13306 */
13307 Utf8Str utf8Name(aName);
13308 uint32_t fFlags = NILFLAG;
13309 if (aFlags)
13310 {
13311 Utf8Str utf8Flags(aFlags);
13312 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13313 AssertRCReturn(vrc, E_INVALIDARG);
13314 }
13315
13316 /*
13317 * Now grab the object lock, validate the state and do the update.
13318 */
13319 AutoCaller autoCaller(this);
13320 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13321
13322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13323
13324 switch (mData->mMachineState)
13325 {
13326 case MachineState_Paused:
13327 case MachineState_Running:
13328 case MachineState_Teleporting:
13329 case MachineState_TeleportingPausedVM:
13330 case MachineState_LiveSnapshotting:
13331 case MachineState_DeletingSnapshotOnline:
13332 case MachineState_DeletingSnapshotPaused:
13333 case MachineState_Saving:
13334 case MachineState_Stopping:
13335 break;
13336
13337 default:
13338 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13339 VBOX_E_INVALID_VM_STATE);
13340 }
13341
13342 setModified(IsModified_MachineData);
13343 mHWData.backup();
13344
13345 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13346 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13347 if (it != mHWData->mGuestProperties.end())
13348 {
13349 if (!fDelete)
13350 {
13351 it->second.strValue = aValue;
13352 it->second.mTimestamp = aTimestamp;
13353 it->second.mFlags = fFlags;
13354 }
13355 else
13356 mHWData->mGuestProperties.erase(it);
13357
13358 mData->mGuestPropertiesModified = TRUE;
13359 }
13360 else if (!fDelete)
13361 {
13362 HWData::GuestProperty prop;
13363 prop.strValue = aValue;
13364 prop.mTimestamp = aTimestamp;
13365 prop.mFlags = fFlags;
13366
13367 mHWData->mGuestProperties[utf8Name] = prop;
13368 mData->mGuestPropertiesModified = TRUE;
13369 }
13370
13371 /*
13372 * Send a callback notification if appropriate
13373 */
13374 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13375 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13376 RTSTR_MAX,
13377 utf8Name.c_str(),
13378 RTSTR_MAX, NULL)
13379 )
13380 {
13381 alock.release();
13382
13383 mParent->onGuestPropertyChange(mData->mUuid,
13384 aName,
13385 aValue,
13386 aFlags);
13387 }
13388 }
13389 catch (...)
13390 {
13391 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13392 }
13393 return S_OK;
13394#else
13395 ReturnComNotImplemented();
13396#endif
13397}
13398
13399STDMETHODIMP SessionMachine::LockMedia()
13400{
13401 AutoCaller autoCaller(this);
13402 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13403
13404 AutoMultiWriteLock2 alock(this->lockHandle(),
13405 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13406
13407 AssertReturn( mData->mMachineState == MachineState_Starting
13408 || mData->mMachineState == MachineState_Restoring
13409 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13410
13411 clearError();
13412 alock.release();
13413 return lockMedia();
13414}
13415
13416STDMETHODIMP SessionMachine::UnlockMedia()
13417{
13418 unlockMedia();
13419 return S_OK;
13420}
13421
13422STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13423 IMediumAttachment **aNewAttachment)
13424{
13425 CheckComArgNotNull(aAttachment);
13426 CheckComArgOutPointerValid(aNewAttachment);
13427
13428 AutoCaller autoCaller(this);
13429 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13430
13431 // request the host lock first, since might be calling Host methods for getting host drives;
13432 // next, protect the media tree all the while we're in here, as well as our member variables
13433 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13434 this->lockHandle(),
13435 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13436
13437 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13438
13439 Bstr ctrlName;
13440 LONG lPort;
13441 LONG lDevice;
13442 bool fTempEject;
13443 {
13444 AutoCaller autoAttachCaller(this);
13445 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13446
13447 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13448
13449 /* Need to query the details first, as the IMediumAttachment reference
13450 * might be to the original settings, which we are going to change. */
13451 ctrlName = pAttach->getControllerName();
13452 lPort = pAttach->getPort();
13453 lDevice = pAttach->getDevice();
13454 fTempEject = pAttach->getTempEject();
13455 }
13456
13457 if (!fTempEject)
13458 {
13459 /* Remember previously mounted medium. The medium before taking the
13460 * backup is not necessarily the same thing. */
13461 ComObjPtr<Medium> oldmedium;
13462 oldmedium = pAttach->getMedium();
13463
13464 setModified(IsModified_Storage);
13465 mMediaData.backup();
13466
13467 // The backup operation makes the pAttach reference point to the
13468 // old settings. Re-get the correct reference.
13469 pAttach = findAttachment(mMediaData->mAttachments,
13470 ctrlName.raw(),
13471 lPort,
13472 lDevice);
13473
13474 {
13475 AutoCaller autoAttachCaller(this);
13476 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13477
13478 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13479 if (!oldmedium.isNull())
13480 oldmedium->removeBackReference(mData->mUuid);
13481
13482 pAttach->updateMedium(NULL);
13483 pAttach->updateEjected();
13484 }
13485
13486 setModified(IsModified_Storage);
13487 }
13488 else
13489 {
13490 {
13491 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13492 pAttach->updateEjected();
13493 }
13494 }
13495
13496 pAttach.queryInterfaceTo(aNewAttachment);
13497
13498 return S_OK;
13499}
13500
13501// public methods only for internal purposes
13502/////////////////////////////////////////////////////////////////////////////
13503
13504/**
13505 * Called from the client watcher thread to check for expected or unexpected
13506 * death of the client process that has a direct session to this machine.
13507 *
13508 * On Win32 and on OS/2, this method is called only when we've got the
13509 * mutex (i.e. the client has either died or terminated normally) so it always
13510 * returns @c true (the client is terminated, the session machine is
13511 * uninitialized).
13512 *
13513 * On other platforms, the method returns @c true if the client process has
13514 * terminated normally or abnormally and the session machine was uninitialized,
13515 * and @c false if the client process is still alive.
13516 *
13517 * @note Locks this object for writing.
13518 */
13519bool SessionMachine::checkForDeath()
13520{
13521 Uninit::Reason reason;
13522 bool terminated = false;
13523
13524 /* Enclose autoCaller with a block because calling uninit() from under it
13525 * will deadlock. */
13526 {
13527 AutoCaller autoCaller(this);
13528 if (!autoCaller.isOk())
13529 {
13530 /* return true if not ready, to cause the client watcher to exclude
13531 * the corresponding session from watching */
13532 LogFlowThisFunc(("Already uninitialized!\n"));
13533 return true;
13534 }
13535
13536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13537
13538 /* Determine the reason of death: if the session state is Closing here,
13539 * everything is fine. Otherwise it means that the client did not call
13540 * OnSessionEnd() before it released the IPC semaphore. This may happen
13541 * either because the client process has abnormally terminated, or
13542 * because it simply forgot to call ISession::Close() before exiting. We
13543 * threat the latter also as an abnormal termination (see
13544 * Session::uninit() for details). */
13545 reason = mData->mSession.mState == SessionState_Unlocking ?
13546 Uninit::Normal :
13547 Uninit::Abnormal;
13548
13549#if defined(RT_OS_WINDOWS)
13550
13551 AssertMsg(mIPCSem, ("semaphore must be created"));
13552
13553 /* release the IPC mutex */
13554 ::ReleaseMutex(mIPCSem);
13555
13556 terminated = true;
13557
13558#elif defined(RT_OS_OS2)
13559
13560 AssertMsg(mIPCSem, ("semaphore must be created"));
13561
13562 /* release the IPC mutex */
13563 ::DosReleaseMutexSem(mIPCSem);
13564
13565 terminated = true;
13566
13567#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13568
13569 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13570
13571 int val = ::semctl(mIPCSem, 0, GETVAL);
13572 if (val > 0)
13573 {
13574 /* the semaphore is signaled, meaning the session is terminated */
13575 terminated = true;
13576 }
13577
13578#else
13579# error "Port me!"
13580#endif
13581
13582 } /* AutoCaller block */
13583
13584 if (terminated)
13585 uninit(reason);
13586
13587 return terminated;
13588}
13589
13590/**
13591 * @note Locks this object for reading.
13592 */
13593HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13594{
13595 LogFlowThisFunc(("\n"));
13596
13597 AutoCaller autoCaller(this);
13598 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13599
13600 ComPtr<IInternalSessionControl> directControl;
13601 {
13602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13603 directControl = mData->mSession.mDirectControl;
13604 }
13605
13606 /* ignore notifications sent after #OnSessionEnd() is called */
13607 if (!directControl)
13608 return S_OK;
13609
13610 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13611}
13612
13613/**
13614 * @note Locks this object for reading.
13615 */
13616HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13617 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13618{
13619 LogFlowThisFunc(("\n"));
13620
13621 AutoCaller autoCaller(this);
13622 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13623
13624 ComPtr<IInternalSessionControl> directControl;
13625 {
13626 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13627 directControl = mData->mSession.mDirectControl;
13628 }
13629
13630 /* ignore notifications sent after #OnSessionEnd() is called */
13631 if (!directControl)
13632 return S_OK;
13633 /*
13634 * instead acting like callback we ask IVirtualBox deliver corresponding event
13635 */
13636
13637 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13638 return S_OK;
13639}
13640
13641/**
13642 * @note Locks this object for reading.
13643 */
13644HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13645{
13646 LogFlowThisFunc(("\n"));
13647
13648 AutoCaller autoCaller(this);
13649 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13650
13651 ComPtr<IInternalSessionControl> directControl;
13652 {
13653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13654 directControl = mData->mSession.mDirectControl;
13655 }
13656
13657 /* ignore notifications sent after #OnSessionEnd() is called */
13658 if (!directControl)
13659 return S_OK;
13660
13661 return directControl->OnSerialPortChange(serialPort);
13662}
13663
13664/**
13665 * @note Locks this object for reading.
13666 */
13667HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13668{
13669 LogFlowThisFunc(("\n"));
13670
13671 AutoCaller autoCaller(this);
13672 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13673
13674 ComPtr<IInternalSessionControl> directControl;
13675 {
13676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13677 directControl = mData->mSession.mDirectControl;
13678 }
13679
13680 /* ignore notifications sent after #OnSessionEnd() is called */
13681 if (!directControl)
13682 return S_OK;
13683
13684 return directControl->OnParallelPortChange(parallelPort);
13685}
13686
13687/**
13688 * @note Locks this object for reading.
13689 */
13690HRESULT SessionMachine::onStorageControllerChange()
13691{
13692 LogFlowThisFunc(("\n"));
13693
13694 AutoCaller autoCaller(this);
13695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13696
13697 ComPtr<IInternalSessionControl> directControl;
13698 {
13699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13700 directControl = mData->mSession.mDirectControl;
13701 }
13702
13703 /* ignore notifications sent after #OnSessionEnd() is called */
13704 if (!directControl)
13705 return S_OK;
13706
13707 return directControl->OnStorageControllerChange();
13708}
13709
13710/**
13711 * @note Locks this object for reading.
13712 */
13713HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13714{
13715 LogFlowThisFunc(("\n"));
13716
13717 AutoCaller autoCaller(this);
13718 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13719
13720 ComPtr<IInternalSessionControl> directControl;
13721 {
13722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13723 directControl = mData->mSession.mDirectControl;
13724 }
13725
13726 /* ignore notifications sent after #OnSessionEnd() is called */
13727 if (!directControl)
13728 return S_OK;
13729
13730 return directControl->OnMediumChange(aAttachment, aForce);
13731}
13732
13733/**
13734 * @note Locks this object for reading.
13735 */
13736HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13737{
13738 LogFlowThisFunc(("\n"));
13739
13740 AutoCaller autoCaller(this);
13741 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13742
13743 ComPtr<IInternalSessionControl> directControl;
13744 {
13745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13746 directControl = mData->mSession.mDirectControl;
13747 }
13748
13749 /* ignore notifications sent after #OnSessionEnd() is called */
13750 if (!directControl)
13751 return S_OK;
13752
13753 return directControl->OnCPUChange(aCPU, aRemove);
13754}
13755
13756HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13757{
13758 LogFlowThisFunc(("\n"));
13759
13760 AutoCaller autoCaller(this);
13761 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13762
13763 ComPtr<IInternalSessionControl> directControl;
13764 {
13765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13766 directControl = mData->mSession.mDirectControl;
13767 }
13768
13769 /* ignore notifications sent after #OnSessionEnd() is called */
13770 if (!directControl)
13771 return S_OK;
13772
13773 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13774}
13775
13776/**
13777 * @note Locks this object for reading.
13778 */
13779HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13780{
13781 LogFlowThisFunc(("\n"));
13782
13783 AutoCaller autoCaller(this);
13784 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13785
13786 ComPtr<IInternalSessionControl> directControl;
13787 {
13788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13789 directControl = mData->mSession.mDirectControl;
13790 }
13791
13792 /* ignore notifications sent after #OnSessionEnd() is called */
13793 if (!directControl)
13794 return S_OK;
13795
13796 return directControl->OnVRDEServerChange(aRestart);
13797}
13798
13799/**
13800 * @note Locks this object for reading.
13801 */
13802HRESULT SessionMachine::onUSBControllerChange()
13803{
13804 LogFlowThisFunc(("\n"));
13805
13806 AutoCaller autoCaller(this);
13807 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13808
13809 ComPtr<IInternalSessionControl> directControl;
13810 {
13811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13812 directControl = mData->mSession.mDirectControl;
13813 }
13814
13815 /* ignore notifications sent after #OnSessionEnd() is called */
13816 if (!directControl)
13817 return S_OK;
13818
13819 return directControl->OnUSBControllerChange();
13820}
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::onSharedFolderChange()
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturnRC(autoCaller.rc());
13831
13832 ComPtr<IInternalSessionControl> directControl;
13833 {
13834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13835 directControl = mData->mSession.mDirectControl;
13836 }
13837
13838 /* ignore notifications sent after #OnSessionEnd() is called */
13839 if (!directControl)
13840 return S_OK;
13841
13842 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13843}
13844
13845/**
13846 * @note Locks this object for reading.
13847 */
13848HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13849{
13850 LogFlowThisFunc(("\n"));
13851
13852 AutoCaller autoCaller(this);
13853 AssertComRCReturnRC(autoCaller.rc());
13854
13855 ComPtr<IInternalSessionControl> directControl;
13856 {
13857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13858 directControl = mData->mSession.mDirectControl;
13859 }
13860
13861 /* ignore notifications sent after #OnSessionEnd() is called */
13862 if (!directControl)
13863 return S_OK;
13864
13865 return directControl->OnClipboardModeChange(aClipboardMode);
13866}
13867
13868/**
13869 * @note Locks this object for reading.
13870 */
13871HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13872{
13873 LogFlowThisFunc(("\n"));
13874
13875 AutoCaller autoCaller(this);
13876 AssertComRCReturnRC(autoCaller.rc());
13877
13878 ComPtr<IInternalSessionControl> directControl;
13879 {
13880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13881 directControl = mData->mSession.mDirectControl;
13882 }
13883
13884 /* ignore notifications sent after #OnSessionEnd() is called */
13885 if (!directControl)
13886 return S_OK;
13887
13888 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13889}
13890
13891/**
13892 * @note Locks this object for reading.
13893 */
13894HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13895{
13896 LogFlowThisFunc(("\n"));
13897
13898 AutoCaller autoCaller(this);
13899 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13900
13901 ComPtr<IInternalSessionControl> directControl;
13902 {
13903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13904 directControl = mData->mSession.mDirectControl;
13905 }
13906
13907 /* ignore notifications sent after #OnSessionEnd() is called */
13908 if (!directControl)
13909 return S_OK;
13910
13911 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13912}
13913
13914/**
13915 * @note Locks this object for reading.
13916 */
13917HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13918{
13919 LogFlowThisFunc(("\n"));
13920
13921 AutoCaller autoCaller(this);
13922 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13923
13924 ComPtr<IInternalSessionControl> directControl;
13925 {
13926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13927 directControl = mData->mSession.mDirectControl;
13928 }
13929
13930 /* ignore notifications sent after #OnSessionEnd() is called */
13931 if (!directControl)
13932 return S_OK;
13933
13934 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13935}
13936
13937/**
13938 * Returns @c true if this machine's USB controller reports it has a matching
13939 * filter for the given USB device and @c false otherwise.
13940 *
13941 * @note locks this object for reading.
13942 */
13943bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13944{
13945 AutoCaller autoCaller(this);
13946 /* silently return if not ready -- this method may be called after the
13947 * direct machine session has been called */
13948 if (!autoCaller.isOk())
13949 return false;
13950
13951#ifdef VBOX_WITH_USB
13952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13953
13954 switch (mData->mMachineState)
13955 {
13956 case MachineState_Starting:
13957 case MachineState_Restoring:
13958 case MachineState_TeleportingIn:
13959 case MachineState_Paused:
13960 case MachineState_Running:
13961 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13962 * elsewhere... */
13963 alock.release();
13964 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13965 default: break;
13966 }
13967#else
13968 NOREF(aDevice);
13969 NOREF(aMaskedIfs);
13970#endif
13971 return false;
13972}
13973
13974/**
13975 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13976 */
13977HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13978 IVirtualBoxErrorInfo *aError,
13979 ULONG aMaskedIfs)
13980{
13981 LogFlowThisFunc(("\n"));
13982
13983 AutoCaller autoCaller(this);
13984
13985 /* This notification may happen after the machine object has been
13986 * uninitialized (the session was closed), so don't assert. */
13987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13988
13989 ComPtr<IInternalSessionControl> directControl;
13990 {
13991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13992 directControl = mData->mSession.mDirectControl;
13993 }
13994
13995 /* fail on notifications sent after #OnSessionEnd() is called, it is
13996 * expected by the caller */
13997 if (!directControl)
13998 return E_FAIL;
13999
14000 /* No locks should be held at this point. */
14001 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14002 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14003
14004 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14005}
14006
14007/**
14008 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14009 */
14010HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14011 IVirtualBoxErrorInfo *aError)
14012{
14013 LogFlowThisFunc(("\n"));
14014
14015 AutoCaller autoCaller(this);
14016
14017 /* This notification may happen after the machine object has been
14018 * uninitialized (the session was closed), so don't assert. */
14019 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14020
14021 ComPtr<IInternalSessionControl> directControl;
14022 {
14023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14024 directControl = mData->mSession.mDirectControl;
14025 }
14026
14027 /* fail on notifications sent after #OnSessionEnd() is called, it is
14028 * expected by the caller */
14029 if (!directControl)
14030 return E_FAIL;
14031
14032 /* No locks should be held at this point. */
14033 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14034 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14035
14036 return directControl->OnUSBDeviceDetach(aId, aError);
14037}
14038
14039// protected methods
14040/////////////////////////////////////////////////////////////////////////////
14041
14042/**
14043 * Helper method to finalize saving the state.
14044 *
14045 * @note Must be called from under this object's lock.
14046 *
14047 * @param aRc S_OK if the snapshot has been taken successfully
14048 * @param aErrMsg human readable error message for failure
14049 *
14050 * @note Locks mParent + this objects for writing.
14051 */
14052HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14053{
14054 LogFlowThisFuncEnter();
14055
14056 AutoCaller autoCaller(this);
14057 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14058
14059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14060
14061 HRESULT rc = S_OK;
14062
14063 if (SUCCEEDED(aRc))
14064 {
14065 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14066
14067 /* save all VM settings */
14068 rc = saveSettings(NULL);
14069 // no need to check whether VirtualBox.xml needs saving also since
14070 // we can't have a name change pending at this point
14071 }
14072 else
14073 {
14074 // delete the saved state file (it might have been already created);
14075 // we need not check whether this is shared with a snapshot here because
14076 // we certainly created this saved state file here anew
14077 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14078 }
14079
14080 /* notify the progress object about operation completion */
14081 Assert(mConsoleTaskData.mProgress);
14082 if (SUCCEEDED(aRc))
14083 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14084 else
14085 {
14086 if (aErrMsg.length())
14087 mConsoleTaskData.mProgress->notifyComplete(aRc,
14088 COM_IIDOF(ISession),
14089 getComponentName(),
14090 aErrMsg.c_str());
14091 else
14092 mConsoleTaskData.mProgress->notifyComplete(aRc);
14093 }
14094
14095 /* clear out the temporary saved state data */
14096 mConsoleTaskData.mLastState = MachineState_Null;
14097 mConsoleTaskData.strStateFilePath.setNull();
14098 mConsoleTaskData.mProgress.setNull();
14099
14100 LogFlowThisFuncLeave();
14101 return rc;
14102}
14103
14104/**
14105 * Deletes the given file if it is no longer in use by either the current machine state
14106 * (if the machine is "saved") or any of the machine's snapshots.
14107 *
14108 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14109 * but is different for each SnapshotMachine. When calling this, the order of calling this
14110 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14111 * is therefore critical. I know, it's all rather messy.
14112 *
14113 * @param strStateFile
14114 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14115 */
14116void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14117 Snapshot *pSnapshotToIgnore)
14118{
14119 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14120 if ( (strStateFile.isNotEmpty())
14121 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14122 )
14123 // ... and it must also not be shared with other snapshots
14124 if ( !mData->mFirstSnapshot
14125 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14126 // this checks the SnapshotMachine's state file paths
14127 )
14128 RTFileDelete(strStateFile.c_str());
14129}
14130
14131/**
14132 * Locks the attached media.
14133 *
14134 * All attached hard disks are locked for writing and DVD/floppy are locked for
14135 * reading. Parents of attached hard disks (if any) are locked for reading.
14136 *
14137 * This method also performs accessibility check of all media it locks: if some
14138 * media is inaccessible, the method will return a failure and a bunch of
14139 * extended error info objects per each inaccessible medium.
14140 *
14141 * Note that this method is atomic: if it returns a success, all media are
14142 * locked as described above; on failure no media is locked at all (all
14143 * succeeded individual locks will be undone).
14144 *
14145 * The caller is responsible for doing the necessary state sanity checks.
14146 *
14147 * The locks made by this method must be undone by calling #unlockMedia() when
14148 * no more needed.
14149 */
14150HRESULT SessionMachine::lockMedia()
14151{
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14154
14155 AutoMultiWriteLock2 alock(this->lockHandle(),
14156 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14157
14158 /* bail out if trying to lock things with already set up locking */
14159 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14160
14161 MultiResult mrc(S_OK);
14162
14163 /* Collect locking information for all medium objects attached to the VM. */
14164 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14165 it != mMediaData->mAttachments.end();
14166 ++it)
14167 {
14168 MediumAttachment* pAtt = *it;
14169 DeviceType_T devType = pAtt->getType();
14170 Medium *pMedium = pAtt->getMedium();
14171
14172 MediumLockList *pMediumLockList(new MediumLockList());
14173 // There can be attachments without a medium (floppy/dvd), and thus
14174 // it's impossible to create a medium lock list. It still makes sense
14175 // to have the empty medium lock list in the map in case a medium is
14176 // attached later.
14177 if (pMedium != NULL)
14178 {
14179 MediumType_T mediumType = pMedium->getType();
14180 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14181 || mediumType == MediumType_Shareable;
14182 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14183
14184 alock.release();
14185 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14186 !fIsReadOnlyLock /* fMediumLockWrite */,
14187 NULL,
14188 *pMediumLockList);
14189 alock.acquire();
14190 if (FAILED(mrc))
14191 {
14192 delete pMediumLockList;
14193 mData->mSession.mLockedMedia.Clear();
14194 break;
14195 }
14196 }
14197
14198 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14199 if (FAILED(rc))
14200 {
14201 mData->mSession.mLockedMedia.Clear();
14202 mrc = setError(rc,
14203 tr("Collecting locking information for all attached media failed"));
14204 break;
14205 }
14206 }
14207
14208 if (SUCCEEDED(mrc))
14209 {
14210 /* Now lock all media. If this fails, nothing is locked. */
14211 alock.release();
14212 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14213 alock.acquire();
14214 if (FAILED(rc))
14215 {
14216 mrc = setError(rc,
14217 tr("Locking of attached media failed"));
14218 }
14219 }
14220
14221 return mrc;
14222}
14223
14224/**
14225 * Undoes the locks made by by #lockMedia().
14226 */
14227void SessionMachine::unlockMedia()
14228{
14229 AutoCaller autoCaller(this);
14230 AssertComRCReturnVoid(autoCaller.rc());
14231
14232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14233
14234 /* we may be holding important error info on the current thread;
14235 * preserve it */
14236 ErrorInfoKeeper eik;
14237
14238 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14239 AssertComRC(rc);
14240}
14241
14242/**
14243 * Helper to change the machine state (reimplementation).
14244 *
14245 * @note Locks this object for writing.
14246 * @note This method must not call saveSettings or SaveSettings, otherwise
14247 * it can cause crashes in random places due to unexpectedly committing
14248 * the current settings. The caller is responsible for that. The call
14249 * to saveStateSettings is fine, because this method does not commit.
14250 */
14251HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14252{
14253 LogFlowThisFuncEnter();
14254 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14255
14256 AutoCaller autoCaller(this);
14257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14258
14259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14260
14261 MachineState_T oldMachineState = mData->mMachineState;
14262
14263 AssertMsgReturn(oldMachineState != aMachineState,
14264 ("oldMachineState=%s, aMachineState=%s\n",
14265 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14266 E_FAIL);
14267
14268 HRESULT rc = S_OK;
14269
14270 int stsFlags = 0;
14271 bool deleteSavedState = false;
14272
14273 /* detect some state transitions */
14274
14275 if ( ( oldMachineState == MachineState_Saved
14276 && aMachineState == MachineState_Restoring)
14277 || ( ( oldMachineState == MachineState_PoweredOff
14278 || oldMachineState == MachineState_Teleported
14279 || oldMachineState == MachineState_Aborted
14280 )
14281 && ( aMachineState == MachineState_TeleportingIn
14282 || aMachineState == MachineState_Starting
14283 )
14284 )
14285 )
14286 {
14287 /* The EMT thread is about to start */
14288
14289 /* Nothing to do here for now... */
14290
14291 /// @todo NEWMEDIA don't let mDVDDrive and other children
14292 /// change anything when in the Starting/Restoring state
14293 }
14294 else if ( ( oldMachineState == MachineState_Running
14295 || oldMachineState == MachineState_Paused
14296 || oldMachineState == MachineState_Teleporting
14297 || oldMachineState == MachineState_LiveSnapshotting
14298 || oldMachineState == MachineState_Stuck
14299 || oldMachineState == MachineState_Starting
14300 || oldMachineState == MachineState_Stopping
14301 || oldMachineState == MachineState_Saving
14302 || oldMachineState == MachineState_Restoring
14303 || oldMachineState == MachineState_TeleportingPausedVM
14304 || oldMachineState == MachineState_TeleportingIn
14305 )
14306 && ( aMachineState == MachineState_PoweredOff
14307 || aMachineState == MachineState_Saved
14308 || aMachineState == MachineState_Teleported
14309 || aMachineState == MachineState_Aborted
14310 )
14311 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14312 * snapshot */
14313 && ( mConsoleTaskData.mSnapshot.isNull()
14314 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14315 )
14316 )
14317 {
14318 /* The EMT thread has just stopped, unlock attached media. Note that as
14319 * opposed to locking that is done from Console, we do unlocking here
14320 * because the VM process may have aborted before having a chance to
14321 * properly unlock all media it locked. */
14322
14323 unlockMedia();
14324 }
14325
14326 if (oldMachineState == MachineState_Restoring)
14327 {
14328 if (aMachineState != MachineState_Saved)
14329 {
14330 /*
14331 * delete the saved state file once the machine has finished
14332 * restoring from it (note that Console sets the state from
14333 * Restoring to Saved if the VM couldn't restore successfully,
14334 * to give the user an ability to fix an error and retry --
14335 * we keep the saved state file in this case)
14336 */
14337 deleteSavedState = true;
14338 }
14339 }
14340 else if ( oldMachineState == MachineState_Saved
14341 && ( aMachineState == MachineState_PoweredOff
14342 || aMachineState == MachineState_Aborted
14343 || aMachineState == MachineState_Teleported
14344 )
14345 )
14346 {
14347 /*
14348 * delete the saved state after Console::ForgetSavedState() is called
14349 * or if the VM process (owning a direct VM session) crashed while the
14350 * VM was Saved
14351 */
14352
14353 /// @todo (dmik)
14354 // Not sure that deleting the saved state file just because of the
14355 // client death before it attempted to restore the VM is a good
14356 // thing. But when it crashes we need to go to the Aborted state
14357 // which cannot have the saved state file associated... The only
14358 // way to fix this is to make the Aborted condition not a VM state
14359 // but a bool flag: i.e., when a crash occurs, set it to true and
14360 // change the state to PoweredOff or Saved depending on the
14361 // saved state presence.
14362
14363 deleteSavedState = true;
14364 mData->mCurrentStateModified = TRUE;
14365 stsFlags |= SaveSTS_CurStateModified;
14366 }
14367
14368 if ( aMachineState == MachineState_Starting
14369 || aMachineState == MachineState_Restoring
14370 || aMachineState == MachineState_TeleportingIn
14371 )
14372 {
14373 /* set the current state modified flag to indicate that the current
14374 * state is no more identical to the state in the
14375 * current snapshot */
14376 if (!mData->mCurrentSnapshot.isNull())
14377 {
14378 mData->mCurrentStateModified = TRUE;
14379 stsFlags |= SaveSTS_CurStateModified;
14380 }
14381 }
14382
14383 if (deleteSavedState)
14384 {
14385 if (mRemoveSavedState)
14386 {
14387 Assert(!mSSData->strStateFilePath.isEmpty());
14388
14389 // it is safe to delete the saved state file if ...
14390 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14391 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14392 // ... none of the snapshots share the saved state file
14393 )
14394 RTFileDelete(mSSData->strStateFilePath.c_str());
14395 }
14396
14397 mSSData->strStateFilePath.setNull();
14398 stsFlags |= SaveSTS_StateFilePath;
14399 }
14400
14401 /* redirect to the underlying peer machine */
14402 mPeer->setMachineState(aMachineState);
14403
14404 if ( aMachineState == MachineState_PoweredOff
14405 || aMachineState == MachineState_Teleported
14406 || aMachineState == MachineState_Aborted
14407 || aMachineState == MachineState_Saved)
14408 {
14409 /* the machine has stopped execution
14410 * (or the saved state file was adopted) */
14411 stsFlags |= SaveSTS_StateTimeStamp;
14412 }
14413
14414 if ( ( oldMachineState == MachineState_PoweredOff
14415 || oldMachineState == MachineState_Aborted
14416 || oldMachineState == MachineState_Teleported
14417 )
14418 && aMachineState == MachineState_Saved)
14419 {
14420 /* the saved state file was adopted */
14421 Assert(!mSSData->strStateFilePath.isEmpty());
14422 stsFlags |= SaveSTS_StateFilePath;
14423 }
14424
14425#ifdef VBOX_WITH_GUEST_PROPS
14426 if ( aMachineState == MachineState_PoweredOff
14427 || aMachineState == MachineState_Aborted
14428 || aMachineState == MachineState_Teleported)
14429 {
14430 /* Make sure any transient guest properties get removed from the
14431 * property store on shutdown. */
14432
14433 HWData::GuestPropertyMap::const_iterator it;
14434 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14435 if (!fNeedsSaving)
14436 for (it = mHWData->mGuestProperties.begin();
14437 it != mHWData->mGuestProperties.end(); ++it)
14438 if ( (it->second.mFlags & guestProp::TRANSIENT)
14439 || (it->second.mFlags & guestProp::TRANSRESET))
14440 {
14441 fNeedsSaving = true;
14442 break;
14443 }
14444 if (fNeedsSaving)
14445 {
14446 mData->mCurrentStateModified = TRUE;
14447 stsFlags |= SaveSTS_CurStateModified;
14448 }
14449 }
14450#endif
14451
14452 rc = saveStateSettings(stsFlags);
14453
14454 if ( ( oldMachineState != MachineState_PoweredOff
14455 && oldMachineState != MachineState_Aborted
14456 && oldMachineState != MachineState_Teleported
14457 )
14458 && ( aMachineState == MachineState_PoweredOff
14459 || aMachineState == MachineState_Aborted
14460 || aMachineState == MachineState_Teleported
14461 )
14462 )
14463 {
14464 /* we've been shut down for any reason */
14465 /* no special action so far */
14466 }
14467
14468 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14469 LogFlowThisFuncLeave();
14470 return rc;
14471}
14472
14473/**
14474 * Sends the current machine state value to the VM process.
14475 *
14476 * @note Locks this object for reading, then calls a client process.
14477 */
14478HRESULT SessionMachine::updateMachineStateOnClient()
14479{
14480 AutoCaller autoCaller(this);
14481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14482
14483 ComPtr<IInternalSessionControl> directControl;
14484 {
14485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14486 AssertReturn(!!mData, E_FAIL);
14487 directControl = mData->mSession.mDirectControl;
14488
14489 /* directControl may be already set to NULL here in #OnSessionEnd()
14490 * called too early by the direct session process while there is still
14491 * some operation (like deleting the snapshot) in progress. The client
14492 * process in this case is waiting inside Session::close() for the
14493 * "end session" process object to complete, while #uninit() called by
14494 * #checkForDeath() on the Watcher thread is waiting for the pending
14495 * operation to complete. For now, we accept this inconsistent behavior
14496 * and simply do nothing here. */
14497
14498 if (mData->mSession.mState == SessionState_Unlocking)
14499 return S_OK;
14500
14501 AssertReturn(!directControl.isNull(), E_FAIL);
14502 }
14503
14504 return directControl->UpdateMachineState(mData->mMachineState);
14505}
Note: See TracBrowser for help on using the repository browser.

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