VirtualBox

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

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

Main/idl: clarify IMachine.videoCaptureFile to be an absolute path, and many naming convention cleanups for method/attribute names starting with an uppercase letter or containing an acronym
Main/xml/Settings.cpp: clean up default video capture file handling and related path conversions, version handling of the new functionality
Main/Machine: handle default value for video capture file better, store relative path in settings if possible, cleanups
Main/src-client/VideoRec.cpp: do not overwrite a file ever
Main/Display: generate a unique name if there is a collision, matching cleanups for name changes
Frontends/VirtualBox: matching name changes
Frontends/VBoxManage: matching name changes, fixing the machine readable output

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 492.6 KB
Line 
1/* $Id: MachineImpl.cpp 46667 2013-06-19 15:30:23Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#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 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
190 mHWVirtExExclusive = false;
191#else
192 mHWVirtExExclusive = true;
193#endif
194#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
195 mPAEEnabled = true;
196#else
197 mPAEEnabled = false;
198#endif
199 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
200 mSyntheticCpu = false;
201 mHPETEnabled = false;
202
203 /* default boot order: floppy - DVD - HDD */
204 mBootOrder[0] = DeviceType_Floppy;
205 mBootOrder[1] = DeviceType_DVD;
206 mBootOrder[2] = DeviceType_HardDisk;
207 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
208 mBootOrder[i] = DeviceType_Null;
209
210 mClipboardMode = ClipboardMode_Disabled;
211 mDragAndDropMode = DragAndDropMode_Disabled;
212 mGuestPropertyNotificationPatterns = "";
213
214 mFirmwareType = FirmwareType_BIOS;
215 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
216 mPointingHIDType = PointingHIDType_PS2Mouse;
217 mChipsetType = ChipsetType_PIIX3;
218 mEmulatedUSBWebcamEnabled = FALSE;
219 mEmulatedUSBCardReaderEnabled = FALSE;
220
221 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
222 mCPUAttached[i] = false;
223
224 mIOCacheEnabled = true;
225 mIOCacheSize = 5; /* 5MB */
226
227 /* Maximum CPU execution cap by default. */
228 mCpuExecutionCap = 100;
229}
230
231Machine::HWData::~HWData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine::HDData structure
237/////////////////////////////////////////////////////////////////////////////
238
239Machine::MediaData::MediaData()
240{
241}
242
243Machine::MediaData::~MediaData()
244{
245}
246
247/////////////////////////////////////////////////////////////////////////////
248// Machine class
249/////////////////////////////////////////////////////////////////////////////
250
251// constructor / destructor
252/////////////////////////////////////////////////////////////////////////////
253
254Machine::Machine()
255 : mCollectorGuest(NULL),
256 mPeer(NULL),
257 mParent(NULL),
258 mSerialPorts(),
259 mParallelPorts(),
260 uRegistryNeedsSaving(0)
261{}
262
263Machine::~Machine()
264{}
265
266HRESULT Machine::FinalConstruct()
267{
268 LogFlowThisFunc(("\n"));
269 return BaseFinalConstruct();
270}
271
272void Machine::FinalRelease()
273{
274 LogFlowThisFunc(("\n"));
275 uninit();
276 BaseFinalRelease();
277}
278
279/**
280 * Initializes a new machine instance; this init() variant creates a new, empty machine.
281 * This gets called from VirtualBox::CreateMachine().
282 *
283 * @param aParent Associated parent object
284 * @param strConfigFile Local file system path to the VM settings file (can
285 * be relative to the VirtualBox config directory).
286 * @param strName name for the machine
287 * @param llGroups list of groups for the machine
288 * @param aOsType OS Type of this machine or NULL.
289 * @param aId UUID for the new machine.
290 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
291 *
292 * @return Success indicator. if not S_OK, the machine object is invalid
293 */
294HRESULT Machine::init(VirtualBox *aParent,
295 const Utf8Str &strConfigFile,
296 const Utf8Str &strName,
297 const StringsList &llGroups,
298 GuestOSType *aOsType,
299 const Guid &aId,
300 bool fForceOverwrite,
301 bool fDirectoryIncludesUUID)
302{
303 LogFlowThisFuncEnter();
304 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
305
306 /* Enclose the state transition NotReady->InInit->Ready */
307 AutoInitSpan autoInitSpan(this);
308 AssertReturn(autoInitSpan.isOk(), E_FAIL);
309
310 HRESULT rc = initImpl(aParent, strConfigFile);
311 if (FAILED(rc)) return rc;
312
313 rc = tryCreateMachineConfigFile(fForceOverwrite);
314 if (FAILED(rc)) return rc;
315
316 if (SUCCEEDED(rc))
317 {
318 // create an empty machine config
319 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
320
321 rc = initDataAndChildObjects();
322 }
323
324 if (SUCCEEDED(rc))
325 {
326 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
327 mData->mAccessible = TRUE;
328
329 unconst(mData->mUuid) = aId;
330
331 mUserData->s.strName = strName;
332
333 mUserData->s.llGroups = llGroups;
334
335 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
336 // the "name sync" flag determines whether the machine directory gets renamed along
337 // with the machine file; say so if the settings file name is the same as the
338 // settings file parent directory (machine directory)
339 mUserData->s.fNameSync = isInOwnDir();
340
341 // initialize the default snapshots folder
342 rc = COMSETTER(SnapshotFolder)(NULL);
343 AssertComRC(rc);
344
345 if (aOsType)
346 {
347 /* Store OS type */
348 mUserData->s.strOsType = aOsType->id();
349
350 /* Apply BIOS defaults */
351 mBIOSSettings->applyDefaults(aOsType);
352
353 /* Apply network adapters defaults */
354 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
355 mNetworkAdapters[slot]->applyDefaults(aOsType);
356
357 /* Apply serial port defaults */
358 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
359 mSerialPorts[slot]->applyDefaults(aOsType);
360
361 /* Let the OS type select 64-bit ness. */
362 mHWData->mLongMode = aOsType->is64Bit()
363 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
364 }
365
366 /* At this point the changing of the current state modification
367 * flag is allowed. */
368 allowStateModification();
369
370 /* commit all changes made during the initialization */
371 commit();
372 }
373
374 /* Confirm a successful initialization when it's the case */
375 if (SUCCEEDED(rc))
376 {
377 if (mData->mAccessible)
378 autoInitSpan.setSucceeded();
379 else
380 autoInitSpan.setLimited();
381 }
382
383 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
384 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
385 mData->mRegistered,
386 mData->mAccessible,
387 rc));
388
389 LogFlowThisFuncLeave();
390
391 return rc;
392}
393
394/**
395 * Initializes a new instance with data from machine XML (formerly Init_Registered).
396 * Gets called in two modes:
397 *
398 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
399 * UUID is specified and we mark the machine as "registered";
400 *
401 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
402 * and the machine remains unregistered until RegisterMachine() is called.
403 *
404 * @param aParent Associated parent object
405 * @param aConfigFile Local file system path to the VM settings file (can
406 * be relative to the VirtualBox config directory).
407 * @param aId UUID of the machine or NULL (see above).
408 *
409 * @return Success indicator. if not S_OK, the machine object is invalid
410 */
411HRESULT Machine::initFromSettings(VirtualBox *aParent,
412 const Utf8Str &strConfigFile,
413 const Guid *aId)
414{
415 LogFlowThisFuncEnter();
416 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
417
418 /* Enclose the state transition NotReady->InInit->Ready */
419 AutoInitSpan autoInitSpan(this);
420 AssertReturn(autoInitSpan.isOk(), E_FAIL);
421
422 HRESULT rc = initImpl(aParent, strConfigFile);
423 if (FAILED(rc)) return rc;
424
425 if (aId)
426 {
427 // loading a registered VM:
428 unconst(mData->mUuid) = *aId;
429 mData->mRegistered = TRUE;
430 // now load the settings from XML:
431 rc = registeredInit();
432 // this calls initDataAndChildObjects() and loadSettings()
433 }
434 else
435 {
436 // opening an unregistered VM (VirtualBox::OpenMachine()):
437 rc = initDataAndChildObjects();
438
439 if (SUCCEEDED(rc))
440 {
441 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
442 mData->mAccessible = TRUE;
443
444 try
445 {
446 // load and parse machine XML; this will throw on XML or logic errors
447 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
448
449 // reject VM UUID duplicates, they can happen if someone
450 // tries to register an already known VM config again
451 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
452 true /* fPermitInaccessible */,
453 false /* aDoSetError */,
454 NULL) != VBOX_E_OBJECT_NOT_FOUND)
455 {
456 throw setError(E_FAIL,
457 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
458 mData->m_strConfigFile.c_str());
459 }
460
461 // use UUID from machine config
462 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
463
464 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
465 NULL /* puuidRegistry */);
466 if (FAILED(rc)) throw rc;
467
468 /* At this point the changing of the current state modification
469 * flag is allowed. */
470 allowStateModification();
471
472 commit();
473 }
474 catch (HRESULT err)
475 {
476 /* we assume that error info is set by the thrower */
477 rc = err;
478 }
479 catch (...)
480 {
481 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
482 }
483 }
484 }
485
486 /* Confirm a successful initialization when it's the case */
487 if (SUCCEEDED(rc))
488 {
489 if (mData->mAccessible)
490 autoInitSpan.setSucceeded();
491 else
492 {
493 autoInitSpan.setLimited();
494
495 // uninit media from this machine's media registry, or else
496 // reloading the settings will fail
497 mParent->unregisterMachineMedia(getId());
498 }
499 }
500
501 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
502 "rc=%08X\n",
503 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
504 mData->mRegistered, mData->mAccessible, rc));
505
506 LogFlowThisFuncLeave();
507
508 return rc;
509}
510
511/**
512 * Initializes a new instance from a machine config that is already in memory
513 * (import OVF case). Since we are importing, the UUID in the machine
514 * config is ignored and we always generate a fresh one.
515 *
516 * @param strName Name for the new machine; this overrides what is specified in config and is used
517 * for the settings file as well.
518 * @param config Machine configuration loaded and parsed from XML.
519 *
520 * @return Success indicator. if not S_OK, the machine object is invalid
521 */
522HRESULT Machine::init(VirtualBox *aParent,
523 const Utf8Str &strName,
524 const settings::MachineConfigFile &config)
525{
526 LogFlowThisFuncEnter();
527
528 /* Enclose the state transition NotReady->InInit->Ready */
529 AutoInitSpan autoInitSpan(this);
530 AssertReturn(autoInitSpan.isOk(), E_FAIL);
531
532 Utf8Str strConfigFile;
533 aParent->getDefaultMachineFolder(strConfigFile);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(RTPATH_DELIMITER);
537 strConfigFile.append(strName);
538 strConfigFile.append(".vbox");
539
540 HRESULT rc = initImpl(aParent, strConfigFile);
541 if (FAILED(rc)) return rc;
542
543 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
544 if (FAILED(rc)) return rc;
545
546 rc = initDataAndChildObjects();
547
548 if (SUCCEEDED(rc))
549 {
550 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
551 mData->mAccessible = TRUE;
552
553 // create empty machine config for instance data
554 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
555
556 // generate fresh UUID, ignore machine config
557 unconst(mData->mUuid).create();
558
559 rc = loadMachineDataFromSettings(config,
560 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
561
562 // override VM name as well, it may be different
563 mUserData->s.strName = strName;
564
565 if (SUCCEEDED(rc))
566 {
567 /* At this point the changing of the current state modification
568 * flag is allowed. */
569 allowStateModification();
570
571 /* commit all changes made during the initialization */
572 commit();
573 }
574 }
575
576 /* Confirm a successful initialization when it's the case */
577 if (SUCCEEDED(rc))
578 {
579 if (mData->mAccessible)
580 autoInitSpan.setSucceeded();
581 else
582 {
583 autoInitSpan.setLimited();
584
585 // uninit media from this machine's media registry, or else
586 // reloading the settings will fail
587 mParent->unregisterMachineMedia(getId());
588 }
589 }
590
591 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
592 "rc=%08X\n",
593 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
594 mData->mRegistered, mData->mAccessible, rc));
595
596 LogFlowThisFuncLeave();
597
598 return rc;
599}
600
601/**
602 * Shared code between the various init() implementations.
603 * @param aParent
604 * @return
605 */
606HRESULT Machine::initImpl(VirtualBox *aParent,
607 const Utf8Str &strConfigFile)
608{
609 LogFlowThisFuncEnter();
610
611 AssertReturn(aParent, E_INVALIDARG);
612 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
613
614 HRESULT rc = S_OK;
615
616 /* share the parent weakly */
617 unconst(mParent) = aParent;
618
619 /* allocate the essential machine data structure (the rest will be
620 * allocated later by initDataAndChildObjects() */
621 mData.allocate();
622
623 /* memorize the config file name (as provided) */
624 mData->m_strConfigFile = strConfigFile;
625
626 /* get the full file name */
627 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
628 if (RT_FAILURE(vrc1))
629 return setError(VBOX_E_FILE_ERROR,
630 tr("Invalid machine settings file name '%s' (%Rrc)"),
631 strConfigFile.c_str(),
632 vrc1);
633
634 LogFlowThisFuncLeave();
635
636 return rc;
637}
638
639/**
640 * Tries to create a machine settings file in the path stored in the machine
641 * instance data. Used when a new machine is created to fail gracefully if
642 * the settings file could not be written (e.g. because machine dir is read-only).
643 * @return
644 */
645HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
646{
647 HRESULT rc = S_OK;
648
649 // when we create a new machine, we must be able to create the settings file
650 RTFILE f = NIL_RTFILE;
651 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
652 if ( RT_SUCCESS(vrc)
653 || vrc == VERR_SHARING_VIOLATION
654 )
655 {
656 if (RT_SUCCESS(vrc))
657 RTFileClose(f);
658 if (!fForceOverwrite)
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Machine settings file '%s' already exists"),
661 mData->m_strConfigFileFull.c_str());
662 else
663 {
664 /* try to delete the config file, as otherwise the creation
665 * of a new settings file will fail. */
666 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
667 if (RT_FAILURE(vrc2))
668 rc = setError(VBOX_E_FILE_ERROR,
669 tr("Could not delete the existing settings file '%s' (%Rrc)"),
670 mData->m_strConfigFileFull.c_str(), vrc2);
671 }
672 }
673 else if ( vrc != VERR_FILE_NOT_FOUND
674 && vrc != VERR_PATH_NOT_FOUND
675 )
676 rc = setError(VBOX_E_FILE_ERROR,
677 tr("Invalid machine settings file name '%s' (%Rrc)"),
678 mData->m_strConfigFileFull.c_str(),
679 vrc);
680 return rc;
681}
682
683/**
684 * Initializes the registered machine by loading the settings file.
685 * This method is separated from #init() in order to make it possible to
686 * retry the operation after VirtualBox startup instead of refusing to
687 * startup the whole VirtualBox server in case if the settings file of some
688 * registered VM is invalid or inaccessible.
689 *
690 * @note Must be always called from this object's write lock
691 * (unless called from #init() that doesn't need any locking).
692 * @note Locks the mUSBController method for writing.
693 * @note Subclasses must not call this method.
694 */
695HRESULT Machine::registeredInit()
696{
697 AssertReturn(!isSessionMachine(), E_FAIL);
698 AssertReturn(!isSnapshotMachine(), E_FAIL);
699 AssertReturn(mData->mUuid.isValid(), E_FAIL);
700 AssertReturn(!mData->mAccessible, E_FAIL);
701
702 HRESULT rc = initDataAndChildObjects();
703
704 if (SUCCEEDED(rc))
705 {
706 /* Temporarily reset the registered flag in order to let setters
707 * potentially called from loadSettings() succeed (isMutable() used in
708 * all setters will return FALSE for a Machine instance if mRegistered
709 * is TRUE). */
710 mData->mRegistered = FALSE;
711
712 try
713 {
714 // load and parse machine XML; this will throw on XML or logic errors
715 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
716
717 if (mData->mUuid != mData->pMachineConfigFile->uuid)
718 throw setError(E_FAIL,
719 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
720 mData->pMachineConfigFile->uuid.raw(),
721 mData->m_strConfigFileFull.c_str(),
722 mData->mUuid.toString().c_str(),
723 mParent->settingsFilePath().c_str());
724
725 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
726 NULL /* const Guid *puuidRegistry */);
727 if (FAILED(rc)) throw rc;
728 }
729 catch (HRESULT err)
730 {
731 /* we assume that error info is set by the thrower */
732 rc = err;
733 }
734 catch (...)
735 {
736 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
737 }
738
739 /* Restore the registered flag (even on failure) */
740 mData->mRegistered = TRUE;
741 }
742
743 if (SUCCEEDED(rc))
744 {
745 /* Set mAccessible to TRUE only if we successfully locked and loaded
746 * the settings file */
747 mData->mAccessible = TRUE;
748
749 /* commit all changes made during loading the settings file */
750 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
751 /// @todo r=klaus for some reason the settings loading logic backs up
752 // the settings, and therefore a commit is needed. Should probably be changed.
753 }
754 else
755 {
756 /* If the machine is registered, then, instead of returning a
757 * failure, we mark it as inaccessible and set the result to
758 * success to give it a try later */
759
760 /* fetch the current error info */
761 mData->mAccessError = com::ErrorInfo();
762 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
763 mData->mUuid.raw(),
764 mData->mAccessError.getText().raw()));
765
766 /* rollback all changes */
767 rollback(false /* aNotify */);
768
769 // uninit media from this machine's media registry, or else
770 // reloading the settings will fail
771 mParent->unregisterMachineMedia(getId());
772
773 /* uninitialize the common part to make sure all data is reset to
774 * default (null) values */
775 uninitDataAndChildObjects();
776
777 rc = S_OK;
778 }
779
780 return rc;
781}
782
783/**
784 * Uninitializes the instance.
785 * Called either from FinalRelease() or by the parent when it gets destroyed.
786 *
787 * @note The caller of this method must make sure that this object
788 * a) doesn't have active callers on the current thread and b) is not locked
789 * by the current thread; otherwise uninit() will hang either a) due to
790 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
791 * a dead-lock caused by this thread waiting for all callers on the other
792 * threads are done but preventing them from doing so by holding a lock.
793 */
794void Machine::uninit()
795{
796 LogFlowThisFuncEnter();
797
798 Assert(!isWriteLockOnCurrentThread());
799
800 Assert(!uRegistryNeedsSaving);
801 if (uRegistryNeedsSaving)
802 {
803 AutoCaller autoCaller(this);
804 if (SUCCEEDED(autoCaller.rc()))
805 {
806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
807 saveSettings(NULL, Machine::SaveS_Force);
808 }
809 }
810
811 /* Enclose the state transition Ready->InUninit->NotReady */
812 AutoUninitSpan autoUninitSpan(this);
813 if (autoUninitSpan.uninitDone())
814 return;
815
816 Assert(!isSnapshotMachine());
817 Assert(!isSessionMachine());
818 Assert(!!mData);
819
820 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
821 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
822
823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
824
825 if (!mData->mSession.mMachine.isNull())
826 {
827 /* Theoretically, this can only happen if the VirtualBox server has been
828 * terminated while there were clients running that owned open direct
829 * sessions. Since in this case we are definitely called by
830 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
831 * won't happen on the client watcher thread (because it does
832 * VirtualBox::addCaller() for the duration of the
833 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
834 * cannot happen until the VirtualBox caller is released). This is
835 * important, because SessionMachine::uninit() cannot correctly operate
836 * after we return from this method (it expects the Machine instance is
837 * still valid). We'll call it ourselves below.
838 */
839 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
840 (SessionMachine*)mData->mSession.mMachine));
841
842 if (Global::IsOnlineOrTransient(mData->mMachineState))
843 {
844 LogWarningThisFunc(("Setting state to Aborted!\n"));
845 /* set machine state using SessionMachine reimplementation */
846 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
847 }
848
849 /*
850 * Uninitialize SessionMachine using public uninit() to indicate
851 * an unexpected uninitialization.
852 */
853 mData->mSession.mMachine->uninit();
854 /* SessionMachine::uninit() must set mSession.mMachine to null */
855 Assert(mData->mSession.mMachine.isNull());
856 }
857
858 // uninit media from this machine's media registry, if they're still there
859 Guid uuidMachine(getId());
860
861 /* the lock is no more necessary (SessionMachine is uninitialized) */
862 alock.release();
863
864 /* XXX This will fail with
865 * "cannot be closed because it is still attached to 1 virtual machines"
866 * because at this point we did not call uninitDataAndChildObjects() yet
867 * and therefore also removeBackReference() for all these mediums was not called! */
868
869 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
870 mParent->unregisterMachineMedia(uuidMachine);
871
872 // has machine been modified?
873 if (mData->flModifications)
874 {
875 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
876 rollback(false /* aNotify */);
877 }
878
879 if (mData->mAccessible)
880 uninitDataAndChildObjects();
881
882 /* free the essential data structure last */
883 mData.free();
884
885 LogFlowThisFuncLeave();
886}
887
888// IMachine properties
889/////////////////////////////////////////////////////////////////////////////
890
891STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
892{
893 CheckComArgOutPointerValid(aParent);
894
895 AutoLimitedCaller autoCaller(this);
896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
897
898 /* mParent is constant during life time, no need to lock */
899 ComObjPtr<VirtualBox> pVirtualBox(mParent);
900 pVirtualBox.queryInterfaceTo(aParent);
901
902 return S_OK;
903}
904
905STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
906{
907 CheckComArgOutPointerValid(aAccessible);
908
909 AutoLimitedCaller autoCaller(this);
910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
911
912 LogFlowThisFunc(("ENTER\n"));
913
914 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
915
916 HRESULT rc = S_OK;
917
918 if (!mData->mAccessible)
919 {
920 /* try to initialize the VM once more if not accessible */
921
922 AutoReinitSpan autoReinitSpan(this);
923 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
924
925#ifdef DEBUG
926 LogFlowThisFunc(("Dumping media backreferences\n"));
927 mParent->dumpAllBackRefs();
928#endif
929
930 if (mData->pMachineConfigFile)
931 {
932 // reset the XML file to force loadSettings() (called from registeredInit())
933 // to parse it again; the file might have changed
934 delete mData->pMachineConfigFile;
935 mData->pMachineConfigFile = NULL;
936 }
937
938 rc = registeredInit();
939
940 if (SUCCEEDED(rc) && mData->mAccessible)
941 {
942 autoReinitSpan.setSucceeded();
943
944 /* make sure interesting parties will notice the accessibility
945 * state change */
946 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
947 mParent->onMachineDataChange(mData->mUuid);
948 }
949 }
950
951 if (SUCCEEDED(rc))
952 *aAccessible = mData->mAccessible;
953
954 LogFlowThisFuncLeave();
955
956 return rc;
957}
958
959STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
960{
961 CheckComArgOutPointerValid(aAccessError);
962
963 AutoLimitedCaller autoCaller(this);
964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
965
966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
967
968 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
969 {
970 /* return shortly */
971 aAccessError = NULL;
972 return S_OK;
973 }
974
975 HRESULT rc = S_OK;
976
977 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
978 rc = errorInfo.createObject();
979 if (SUCCEEDED(rc))
980 {
981 errorInfo->init(mData->mAccessError.getResultCode(),
982 mData->mAccessError.getInterfaceID().ref(),
983 Utf8Str(mData->mAccessError.getComponent()).c_str(),
984 Utf8Str(mData->mAccessError.getText()));
985 rc = errorInfo.queryInterfaceTo(aAccessError);
986 }
987
988 return rc;
989}
990
991STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
992{
993 CheckComArgOutPointerValid(aName);
994
995 AutoCaller autoCaller(this);
996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
997
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 mUserData->s.strName.cloneTo(aName);
1001
1002 return S_OK;
1003}
1004
1005STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1006{
1007 CheckComArgStrNotEmptyOrNull(aName);
1008
1009 AutoCaller autoCaller(this);
1010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1011
1012 // prohibit setting a UUID only as the machine name, or else it can
1013 // never be found by findMachine()
1014 Guid test(aName);
1015
1016 if (test.isValid())
1017 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1018
1019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1020
1021 HRESULT rc = checkStateDependency(MutableStateDep);
1022 if (FAILED(rc)) return rc;
1023
1024 setModified(IsModified_MachineData);
1025 mUserData.backup();
1026 mUserData->s.strName = aName;
1027
1028 return S_OK;
1029}
1030
1031STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1032{
1033 CheckComArgOutPointerValid(aDescription);
1034
1035 AutoCaller autoCaller(this);
1036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1037
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 mUserData->s.strDescription.cloneTo(aDescription);
1041
1042 return S_OK;
1043}
1044
1045STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1046{
1047 AutoCaller autoCaller(this);
1048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1049
1050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1051
1052 // this can be done in principle in any state as it doesn't affect the VM
1053 // significantly, but play safe by not messing around while complex
1054 // activities are going on
1055 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1056 if (FAILED(rc)) return rc;
1057
1058 setModified(IsModified_MachineData);
1059 mUserData.backup();
1060 mUserData->s.strDescription = aDescription;
1061
1062 return S_OK;
1063}
1064
1065STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1066{
1067 CheckComArgOutPointerValid(aId);
1068
1069 AutoLimitedCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073
1074 mData->mUuid.toUtf16().cloneTo(aId);
1075
1076 return S_OK;
1077}
1078
1079STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1080{
1081 CheckComArgOutSafeArrayPointerValid(aGroups);
1082
1083 AutoCaller autoCaller(this);
1084 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1085
1086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1087 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1088 size_t i = 0;
1089 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1090 it != mUserData->s.llGroups.end();
1091 ++it, i++)
1092 {
1093 Bstr tmp = *it;
1094 tmp.cloneTo(&groups[i]);
1095 }
1096 groups.detachTo(ComSafeArrayOutArg(aGroups));
1097
1098 return S_OK;
1099}
1100
1101STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1102{
1103 AutoCaller autoCaller(this);
1104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1105
1106 StringsList llGroups;
1107 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1108 if (FAILED(rc))
1109 return rc;
1110
1111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 // changing machine groups is possible while the VM is offline
1114 rc = checkStateDependency(OfflineStateDep);
1115 if (FAILED(rc)) return rc;
1116
1117 setModified(IsModified_MachineData);
1118 mUserData.backup();
1119 mUserData->s.llGroups = llGroups;
1120
1121 return S_OK;
1122}
1123
1124STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1125{
1126 CheckComArgOutPointerValid(aOSTypeId);
1127
1128 AutoCaller autoCaller(this);
1129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1130
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 mUserData->s.strOsType.cloneTo(aOSTypeId);
1134
1135 return S_OK;
1136}
1137
1138STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1139{
1140 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1141
1142 AutoCaller autoCaller(this);
1143 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1144
1145 /* look up the object by Id to check it is valid */
1146 ComPtr<IGuestOSType> guestOSType;
1147 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1148 if (FAILED(rc)) return rc;
1149
1150 /* when setting, always use the "etalon" value for consistency -- lookup
1151 * by ID is case-insensitive and the input value may have different case */
1152 Bstr osTypeId;
1153 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1154 if (FAILED(rc)) return rc;
1155
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 rc = checkStateDependency(MutableStateDep);
1159 if (FAILED(rc)) return rc;
1160
1161 setModified(IsModified_MachineData);
1162 mUserData.backup();
1163 mUserData->s.strOsType = osTypeId;
1164
1165 return S_OK;
1166}
1167
1168
1169STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1170{
1171 CheckComArgOutPointerValid(aFirmwareType);
1172
1173 AutoCaller autoCaller(this);
1174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1175
1176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 *aFirmwareType = mHWData->mFirmwareType;
1179
1180 return S_OK;
1181}
1182
1183STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1184{
1185 AutoCaller autoCaller(this);
1186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1188
1189 HRESULT rc = checkStateDependency(MutableStateDep);
1190 if (FAILED(rc)) return rc;
1191
1192 setModified(IsModified_MachineData);
1193 mHWData.backup();
1194 mHWData->mFirmwareType = aFirmwareType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1200{
1201 CheckComArgOutPointerValid(aKeyboardHIDType);
1202
1203 AutoCaller autoCaller(this);
1204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1205
1206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1207
1208 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1214{
1215 AutoCaller autoCaller(this);
1216 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1218
1219 HRESULT rc = checkStateDependency(MutableStateDep);
1220 if (FAILED(rc)) return rc;
1221
1222 setModified(IsModified_MachineData);
1223 mHWData.backup();
1224 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1225
1226 return S_OK;
1227}
1228
1229STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1230{
1231 CheckComArgOutPointerValid(aPointingHIDType);
1232
1233 AutoCaller autoCaller(this);
1234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1235
1236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1237
1238 *aPointingHIDType = mHWData->mPointingHIDType;
1239
1240 return S_OK;
1241}
1242
1243STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1244{
1245 AutoCaller autoCaller(this);
1246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 HRESULT rc = checkStateDependency(MutableStateDep);
1250 if (FAILED(rc)) return rc;
1251
1252 setModified(IsModified_MachineData);
1253 mHWData.backup();
1254 mHWData->mPointingHIDType = aPointingHIDType;
1255
1256 return S_OK;
1257}
1258
1259STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1260{
1261 CheckComArgOutPointerValid(aChipsetType);
1262
1263 AutoCaller autoCaller(this);
1264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1265
1266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 *aChipsetType = mHWData->mChipsetType;
1269
1270 return S_OK;
1271}
1272
1273STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1274{
1275 AutoCaller autoCaller(this);
1276 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1278
1279 HRESULT rc = checkStateDependency(MutableStateDep);
1280 if (FAILED(rc)) return rc;
1281
1282 if (aChipsetType != mHWData->mChipsetType)
1283 {
1284 setModified(IsModified_MachineData);
1285 mHWData.backup();
1286 mHWData->mChipsetType = aChipsetType;
1287
1288 // Resize network adapter array, to be finalized on commit/rollback.
1289 // We must not throw away entries yet, otherwise settings are lost
1290 // without a way to roll back.
1291 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1292 size_t oldCount = mNetworkAdapters.size();
1293 if (newCount > oldCount)
1294 {
1295 mNetworkAdapters.resize(newCount);
1296 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1297 {
1298 unconst(mNetworkAdapters[slot]).createObject();
1299 mNetworkAdapters[slot]->init(this, slot);
1300 }
1301 }
1302 }
1303
1304 return S_OK;
1305}
1306
1307STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1308{
1309 CheckComArgOutPointerValid(aHWVersion);
1310
1311 AutoCaller autoCaller(this);
1312 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1313
1314 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1315
1316 mHWData->mHWVersion.cloneTo(aHWVersion);
1317
1318 return S_OK;
1319}
1320
1321STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1322{
1323 /* check known version */
1324 Utf8Str hwVersion = aHWVersion;
1325 if ( hwVersion.compare("1") != 0
1326 && hwVersion.compare("2") != 0)
1327 return setError(E_INVALIDARG,
1328 tr("Invalid hardware version: %ls\n"), aHWVersion);
1329
1330 AutoCaller autoCaller(this);
1331 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1332
1333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1334
1335 HRESULT rc = checkStateDependency(MutableStateDep);
1336 if (FAILED(rc)) return rc;
1337
1338 setModified(IsModified_MachineData);
1339 mHWData.backup();
1340 mHWData->mHWVersion = hwVersion;
1341
1342 return S_OK;
1343}
1344
1345STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1346{
1347 CheckComArgOutPointerValid(aUUID);
1348
1349 AutoCaller autoCaller(this);
1350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1351
1352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1353
1354 if (mHWData->mHardwareUUID.isValid())
1355 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1356 else
1357 mData->mUuid.toUtf16().cloneTo(aUUID);
1358
1359 return S_OK;
1360}
1361
1362STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1363{
1364 Guid hardwareUUID(aUUID);
1365 if (!hardwareUUID.isValid())
1366 return E_INVALIDARG;
1367
1368 AutoCaller autoCaller(this);
1369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1370
1371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1372
1373 HRESULT rc = checkStateDependency(MutableStateDep);
1374 if (FAILED(rc)) return rc;
1375
1376 setModified(IsModified_MachineData);
1377 mHWData.backup();
1378 if (hardwareUUID == mData->mUuid)
1379 mHWData->mHardwareUUID.clear();
1380 else
1381 mHWData->mHardwareUUID = hardwareUUID;
1382
1383 return S_OK;
1384}
1385
1386STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1387{
1388 CheckComArgOutPointerValid(memorySize);
1389
1390 AutoCaller autoCaller(this);
1391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1392
1393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1394
1395 *memorySize = mHWData->mMemorySize;
1396
1397 return S_OK;
1398}
1399
1400STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1401{
1402 /* check RAM limits */
1403 if ( memorySize < MM_RAM_MIN_IN_MB
1404 || memorySize > MM_RAM_MAX_IN_MB
1405 )
1406 return setError(E_INVALIDARG,
1407 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1408 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1409
1410 AutoCaller autoCaller(this);
1411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1412
1413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1414
1415 HRESULT rc = checkStateDependency(MutableStateDep);
1416 if (FAILED(rc)) return rc;
1417
1418 setModified(IsModified_MachineData);
1419 mHWData.backup();
1420 mHWData->mMemorySize = memorySize;
1421
1422 return S_OK;
1423}
1424
1425STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1426{
1427 CheckComArgOutPointerValid(CPUCount);
1428
1429 AutoCaller autoCaller(this);
1430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1431
1432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1433
1434 *CPUCount = mHWData->mCPUCount;
1435
1436 return S_OK;
1437}
1438
1439STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1440{
1441 /* check CPU limits */
1442 if ( CPUCount < SchemaDefs::MinCPUCount
1443 || CPUCount > SchemaDefs::MaxCPUCount
1444 )
1445 return setError(E_INVALIDARG,
1446 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1447 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1448
1449 AutoCaller autoCaller(this);
1450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1451
1452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1455 if (mHWData->mCPUHotPlugEnabled)
1456 {
1457 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1458 {
1459 if (mHWData->mCPUAttached[idx])
1460 return setError(E_INVALIDARG,
1461 tr("There is still a CPU attached to socket %lu."
1462 "Detach the CPU before removing the socket"),
1463 CPUCount, idx+1);
1464 }
1465 }
1466
1467 HRESULT rc = checkStateDependency(MutableStateDep);
1468 if (FAILED(rc)) return rc;
1469
1470 setModified(IsModified_MachineData);
1471 mHWData.backup();
1472 mHWData->mCPUCount = CPUCount;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1478{
1479 CheckComArgOutPointerValid(aExecutionCap);
1480
1481 AutoCaller autoCaller(this);
1482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1483
1484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1485
1486 *aExecutionCap = mHWData->mCpuExecutionCap;
1487
1488 return S_OK;
1489}
1490
1491STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1492{
1493 HRESULT rc = S_OK;
1494
1495 /* check throttle limits */
1496 if ( aExecutionCap < 1
1497 || aExecutionCap > 100
1498 )
1499 return setError(E_INVALIDARG,
1500 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1501 aExecutionCap, 1, 100);
1502
1503 AutoCaller autoCaller(this);
1504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1505
1506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 alock.release();
1509 rc = onCPUExecutionCapChange(aExecutionCap);
1510 alock.acquire();
1511 if (FAILED(rc)) return rc;
1512
1513 setModified(IsModified_MachineData);
1514 mHWData.backup();
1515 mHWData->mCpuExecutionCap = aExecutionCap;
1516
1517 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1518 if (Global::IsOnline(mData->mMachineState))
1519 saveSettings(NULL);
1520
1521 return S_OK;
1522}
1523
1524
1525STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1526{
1527 CheckComArgOutPointerValid(aEnabled);
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 *aEnabled = mHWData->mCPUHotPlugEnabled;
1535
1536 return S_OK;
1537}
1538
1539STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1540{
1541 HRESULT rc = S_OK;
1542
1543 AutoCaller autoCaller(this);
1544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 rc = checkStateDependency(MutableStateDep);
1549 if (FAILED(rc)) return rc;
1550
1551 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1552 {
1553 if (aEnabled)
1554 {
1555 setModified(IsModified_MachineData);
1556 mHWData.backup();
1557
1558 /* Add the amount of CPUs currently attached */
1559 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1560 {
1561 mHWData->mCPUAttached[i] = true;
1562 }
1563 }
1564 else
1565 {
1566 /*
1567 * We can disable hotplug only if the amount of maximum CPUs is equal
1568 * to the amount of attached CPUs
1569 */
1570 unsigned cCpusAttached = 0;
1571 unsigned iHighestId = 0;
1572
1573 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1574 {
1575 if (mHWData->mCPUAttached[i])
1576 {
1577 cCpusAttached++;
1578 iHighestId = i;
1579 }
1580 }
1581
1582 if ( (cCpusAttached != mHWData->mCPUCount)
1583 || (iHighestId >= mHWData->mCPUCount))
1584 return setError(E_INVALIDARG,
1585 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1586
1587 setModified(IsModified_MachineData);
1588 mHWData.backup();
1589 }
1590 }
1591
1592 mHWData->mCPUHotPlugEnabled = aEnabled;
1593
1594 return rc;
1595}
1596
1597STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1598{
1599#ifdef VBOX_WITH_USB_CARDREADER
1600 CheckComArgOutPointerValid(aEnabled);
1601
1602 AutoCaller autoCaller(this);
1603 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1604
1605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1606
1607 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1608
1609 return S_OK;
1610#else
1611 NOREF(aEnabled);
1612 return E_NOTIMPL;
1613#endif
1614}
1615
1616STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1617{
1618#ifdef VBOX_WITH_USB_CARDREADER
1619 AutoCaller autoCaller(this);
1620 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1622
1623 HRESULT rc = checkStateDependency(MutableStateDep);
1624 if (FAILED(rc)) return rc;
1625
1626 setModified(IsModified_MachineData);
1627 mHWData.backup();
1628 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1629
1630 return S_OK;
1631#else
1632 NOREF(aEnabled);
1633 return E_NOTIMPL;
1634#endif
1635}
1636
1637STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1638{
1639#ifdef VBOX_WITH_USB_VIDEO
1640 CheckComArgOutPointerValid(aEnabled);
1641
1642 AutoCaller autoCaller(this);
1643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1644
1645 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1648
1649 return S_OK;
1650#else
1651 NOREF(aEnabled);
1652 return E_NOTIMPL;
1653#endif
1654}
1655
1656STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1657{
1658#ifdef VBOX_WITH_USB_VIDEO
1659 AutoCaller autoCaller(this);
1660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
1663 HRESULT rc = checkStateDependency(MutableStateDep);
1664 if (FAILED(rc)) return rc;
1665
1666 setModified(IsModified_MachineData);
1667 mHWData.backup();
1668 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1669
1670 return S_OK;
1671#else
1672 NOREF(aEnabled);
1673 return E_NOTIMPL;
1674#endif
1675}
1676
1677STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1678{
1679 CheckComArgOutPointerValid(aEnabled);
1680
1681 AutoCaller autoCaller(this);
1682 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1684
1685 *aEnabled = mHWData->mHPETEnabled;
1686
1687 return S_OK;
1688}
1689
1690STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1691{
1692 HRESULT rc = S_OK;
1693
1694 AutoCaller autoCaller(this);
1695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 rc = checkStateDependency(MutableStateDep);
1699 if (FAILED(rc)) return rc;
1700
1701 setModified(IsModified_MachineData);
1702 mHWData.backup();
1703
1704 mHWData->mHPETEnabled = aEnabled;
1705
1706 return rc;
1707}
1708
1709STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1710{
1711 AutoCaller autoCaller(this);
1712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1713
1714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1715
1716 *fEnabled = mHWData->mVideoCaptureEnabled;
1717 return S_OK;
1718}
1719
1720STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1721{
1722 HRESULT rc = S_OK;
1723
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1727
1728 setModified(IsModified_MachineData);
1729 mHWData.backup();
1730 mHWData->mVideoCaptureEnabled = fEnabled;
1731
1732 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1733 if (Global::IsOnline(mData->mMachineState))
1734 saveSettings(NULL);
1735
1736 alock.release();
1737 rc = onVideoCaptureChange();
1738
1739 return rc;
1740}
1741
1742STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1743{
1744 CheckComArgOutSafeArrayPointerValid(aScreens);
1745
1746 AutoCaller autoCaller(this);
1747 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1748
1749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1750
1751 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1752 for (unsigned i = 0; i < screens.size(); i++)
1753 screens[i] = mHWData->maVideoCaptureScreens[i];
1754 screens.detachTo(ComSafeArrayOutArg(aScreens));
1755 return S_OK;
1756}
1757
1758STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1759{
1760 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1761 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1762 bool fChanged = false;
1763
1764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1765
1766 for (unsigned i = 0; i < screens.size(); i++)
1767 {
1768 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1769 {
1770 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1771 fChanged = true;
1772 }
1773 }
1774 if (fChanged)
1775 {
1776 alock.release();
1777 HRESULT rc = onVideoCaptureChange();
1778 alock.acquire();
1779 if (FAILED(rc)) return rc;
1780 setModified(IsModified_MachineData);
1781
1782 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1783 if (Global::IsOnline(mData->mMachineState))
1784 saveSettings(NULL);
1785 }
1786
1787 return S_OK;
1788}
1789
1790STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1791{
1792 AutoCaller autoCaller(this);
1793 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1794
1795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1796 if (mHWData->mVideoCaptureFile.isEmpty())
1797 {
1798 Utf8Str defaultFile;
1799 getDefaultVideoCaptureFile(defaultFile);
1800 defaultFile.cloneTo(apFile);
1801 }
1802 else
1803 mHWData->mVideoCaptureFile.cloneTo(apFile);
1804 return S_OK;
1805}
1806
1807STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1808{
1809 Utf8Str strFile(aFile);
1810 AutoCaller autoCaller(this);
1811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1812
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1838{
1839 AutoCaller autoCaller(this);
1840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1841
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aHorzRes = mHWData->mVideoCaptureWidth;
1844 return S_OK;
1845}
1846
1847STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1848{
1849 AutoCaller autoCaller(this);
1850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1851
1852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1853
1854 if ( Global::IsOnline(mData->mMachineState)
1855 && mHWData->mVideoCaptureEnabled)
1856 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1857
1858 setModified(IsModified_MachineData);
1859 mHWData.backup();
1860 mHWData->mVideoCaptureWidth = aHorzRes;
1861
1862 return S_OK;
1863}
1864
1865STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1866{
1867 AutoCaller autoCaller(this);
1868 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1869
1870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1871 *aVertRes = mHWData->mVideoCaptureHeight;
1872 return S_OK;
1873}
1874
1875STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1876{
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 if ( Global::IsOnline(mData->mMachineState)
1883 && mHWData->mVideoCaptureEnabled)
1884 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1885
1886 setModified(IsModified_MachineData);
1887 mHWData.backup();
1888 mHWData->mVideoCaptureHeight = aVertRes;
1889
1890 return S_OK;
1891}
1892
1893STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1894{
1895 AutoCaller autoCaller(this);
1896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1897
1898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1899 *aRate = mHWData->mVideoCaptureRate;
1900 return S_OK;
1901}
1902
1903STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1904{
1905 AutoCaller autoCaller(this);
1906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1907
1908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1909
1910 if ( Global::IsOnline(mData->mMachineState)
1911 && mHWData->mVideoCaptureEnabled)
1912 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1913
1914 setModified(IsModified_MachineData);
1915 mHWData.backup();
1916 mHWData->mVideoCaptureRate = aRate;
1917
1918 return S_OK;
1919}
1920
1921STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1922{
1923 AutoCaller autoCaller(this);
1924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1925
1926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1927 *aFPS = mHWData->mVideoCaptureFPS;
1928 return S_OK;
1929}
1930
1931STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1932{
1933 AutoCaller autoCaller(this);
1934 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1935
1936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1937
1938 if ( Global::IsOnline(mData->mMachineState)
1939 && mHWData->mVideoCaptureEnabled)
1940 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1941
1942 setModified(IsModified_MachineData);
1943 mHWData.backup();
1944 mHWData->mVideoCaptureFPS = aFPS;
1945
1946 return S_OK;
1947}
1948
1949STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1950{
1951 CheckComArgOutPointerValid(aGraphicsControllerType);
1952
1953 AutoCaller autoCaller(this);
1954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1955
1956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1959
1960 return S_OK;
1961}
1962
1963STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1964{
1965 switch (aGraphicsControllerType)
1966 {
1967 case GraphicsControllerType_Null:
1968 case GraphicsControllerType_VBoxVGA:
1969 break;
1970 default:
1971 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1972 }
1973
1974 AutoCaller autoCaller(this);
1975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1976
1977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 HRESULT rc = checkStateDependency(MutableStateDep);
1980 if (FAILED(rc)) return rc;
1981
1982 setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1985
1986 return S_OK;
1987}
1988
1989STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1990{
1991 CheckComArgOutPointerValid(memorySize);
1992
1993 AutoCaller autoCaller(this);
1994 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1995
1996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1997
1998 *memorySize = mHWData->mVRAMSize;
1999
2000 return S_OK;
2001}
2002
2003STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2004{
2005 /* check VRAM limits */
2006 if (memorySize < SchemaDefs::MinGuestVRAM ||
2007 memorySize > SchemaDefs::MaxGuestVRAM)
2008 return setError(E_INVALIDARG,
2009 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2010 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2011
2012 AutoCaller autoCaller(this);
2013 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mVRAMSize = memorySize;
2023
2024 return S_OK;
2025}
2026
2027/** @todo this method should not be public */
2028STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2029{
2030 CheckComArgOutPointerValid(memoryBalloonSize);
2031
2032 AutoCaller autoCaller(this);
2033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2034
2035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2036
2037 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2038
2039 return S_OK;
2040}
2041
2042/**
2043 * Set the memory balloon size.
2044 *
2045 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2046 * we have to make sure that we never call IGuest from here.
2047 */
2048STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2049{
2050 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2051#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2052 /* check limits */
2053 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2054 return setError(E_INVALIDARG,
2055 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2056 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2057
2058 AutoCaller autoCaller(this);
2059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2060
2061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2062
2063 setModified(IsModified_MachineData);
2064 mHWData.backup();
2065 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2066
2067 return S_OK;
2068#else
2069 NOREF(memoryBalloonSize);
2070 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2071#endif
2072}
2073
2074STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2075{
2076 CheckComArgOutPointerValid(aEnabled);
2077
2078 AutoCaller autoCaller(this);
2079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2080
2081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2082
2083 *aEnabled = mHWData->mPageFusionEnabled;
2084 return S_OK;
2085}
2086
2087STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2088{
2089#ifdef VBOX_WITH_PAGE_SHARING
2090 AutoCaller autoCaller(this);
2091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094
2095 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2096 setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mPageFusionEnabled = aEnabled;
2099 return S_OK;
2100#else
2101 NOREF(aEnabled);
2102 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2103#endif
2104}
2105
2106STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2107{
2108 CheckComArgOutPointerValid(aEnabled);
2109
2110 AutoCaller autoCaller(this);
2111 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2112
2113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2114
2115 *aEnabled = mHWData->mAccelerate3DEnabled;
2116
2117 return S_OK;
2118}
2119
2120STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2121{
2122 AutoCaller autoCaller(this);
2123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2124
2125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2126
2127 HRESULT rc = checkStateDependency(MutableStateDep);
2128 if (FAILED(rc)) return rc;
2129
2130 /** @todo check validity! */
2131
2132 setModified(IsModified_MachineData);
2133 mHWData.backup();
2134 mHWData->mAccelerate3DEnabled = enable;
2135
2136 return S_OK;
2137}
2138
2139
2140STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2141{
2142 CheckComArgOutPointerValid(aEnabled);
2143
2144 AutoCaller autoCaller(this);
2145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2146
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2155{
2156 AutoCaller autoCaller(this);
2157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2158
2159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 HRESULT rc = checkStateDependency(MutableStateDep);
2162 if (FAILED(rc)) return rc;
2163
2164 /** @todo check validity! */
2165
2166 setModified(IsModified_MachineData);
2167 mHWData.backup();
2168 mHWData->mAccelerate2DVideoEnabled = enable;
2169
2170 return S_OK;
2171}
2172
2173STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2174{
2175 CheckComArgOutPointerValid(monitorCount);
2176
2177 AutoCaller autoCaller(this);
2178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2179
2180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2181
2182 *monitorCount = mHWData->mMonitorCount;
2183
2184 return S_OK;
2185}
2186
2187STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2188{
2189 /* make sure monitor count is a sensible number */
2190 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2191 return setError(E_INVALIDARG,
2192 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2193 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2194
2195 AutoCaller autoCaller(this);
2196 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2197
2198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 HRESULT rc = checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mMonitorCount = monitorCount;
2206
2207 return S_OK;
2208}
2209
2210STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2211{
2212 CheckComArgOutPointerValid(biosSettings);
2213
2214 AutoCaller autoCaller(this);
2215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2216
2217 /* mBIOSSettings is constant during life time, no need to lock */
2218 mBIOSSettings.queryInterfaceTo(biosSettings);
2219
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2224{
2225 CheckComArgOutPointerValid(aVal);
2226
2227 AutoCaller autoCaller(this);
2228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
2230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2231
2232 switch (property)
2233 {
2234 case CPUPropertyType_PAE:
2235 *aVal = mHWData->mPAEEnabled;
2236 break;
2237
2238 case CPUPropertyType_Synthetic:
2239 *aVal = mHWData->mSyntheticCpu;
2240 break;
2241
2242 case CPUPropertyType_LongMode:
2243 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2244 *aVal = TRUE;
2245 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2246 *aVal = FALSE;
2247#if HC_ARCH_BITS == 64
2248 else
2249 *aVal = TRUE;
2250#else
2251 else
2252 {
2253 *aVal = FALSE;
2254
2255 ComPtr<IGuestOSType> ptrGuestOSType;
2256 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2257 if (SUCCEEDED(hrc2))
2258 {
2259 BOOL fIs64Bit = FALSE;
2260 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2261 if (SUCCEEDED(hrc2) && fIs64Bit)
2262 {
2263 ComObjPtr<Host> ptrHost = mParent->host();
2264 alock.release();
2265
2266 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2267 if (FAILED(hrc2))
2268 *aVal = FALSE;
2269 }
2270 }
2271 }
2272#endif
2273 break;
2274
2275 default:
2276 return E_INVALIDARG;
2277 }
2278 return S_OK;
2279}
2280
2281STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2282{
2283 AutoCaller autoCaller(this);
2284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2285
2286 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2287
2288 HRESULT rc = checkStateDependency(MutableStateDep);
2289 if (FAILED(rc)) return rc;
2290
2291 switch (property)
2292 {
2293 case CPUPropertyType_PAE:
2294 setModified(IsModified_MachineData);
2295 mHWData.backup();
2296 mHWData->mPAEEnabled = !!aVal;
2297 break;
2298
2299 case CPUPropertyType_Synthetic:
2300 setModified(IsModified_MachineData);
2301 mHWData.backup();
2302 mHWData->mSyntheticCpu = !!aVal;
2303 break;
2304
2305 case CPUPropertyType_LongMode:
2306 setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2309 break;
2310
2311 default:
2312 return E_INVALIDARG;
2313 }
2314 return S_OK;
2315}
2316
2317STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2318{
2319 CheckComArgOutPointerValid(aValEax);
2320 CheckComArgOutPointerValid(aValEbx);
2321 CheckComArgOutPointerValid(aValEcx);
2322 CheckComArgOutPointerValid(aValEdx);
2323
2324 AutoCaller autoCaller(this);
2325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2326
2327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2328
2329 switch(aId)
2330 {
2331 case 0x0:
2332 case 0x1:
2333 case 0x2:
2334 case 0x3:
2335 case 0x4:
2336 case 0x5:
2337 case 0x6:
2338 case 0x7:
2339 case 0x8:
2340 case 0x9:
2341 case 0xA:
2342 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2343 return E_INVALIDARG;
2344
2345 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2346 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2347 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2348 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2349 break;
2350
2351 case 0x80000000:
2352 case 0x80000001:
2353 case 0x80000002:
2354 case 0x80000003:
2355 case 0x80000004:
2356 case 0x80000005:
2357 case 0x80000006:
2358 case 0x80000007:
2359 case 0x80000008:
2360 case 0x80000009:
2361 case 0x8000000A:
2362 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2363 return E_INVALIDARG;
2364
2365 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2366 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2367 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2368 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2369 break;
2370
2371 default:
2372 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2373 }
2374 return S_OK;
2375}
2376
2377STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2378{
2379 AutoCaller autoCaller(this);
2380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2381
2382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2383
2384 HRESULT rc = checkStateDependency(MutableStateDep);
2385 if (FAILED(rc)) return rc;
2386
2387 switch(aId)
2388 {
2389 case 0x0:
2390 case 0x1:
2391 case 0x2:
2392 case 0x3:
2393 case 0x4:
2394 case 0x5:
2395 case 0x6:
2396 case 0x7:
2397 case 0x8:
2398 case 0x9:
2399 case 0xA:
2400 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2401 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2402 setModified(IsModified_MachineData);
2403 mHWData.backup();
2404 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2405 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2406 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2407 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2408 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2409 break;
2410
2411 case 0x80000000:
2412 case 0x80000001:
2413 case 0x80000002:
2414 case 0x80000003:
2415 case 0x80000004:
2416 case 0x80000005:
2417 case 0x80000006:
2418 case 0x80000007:
2419 case 0x80000008:
2420 case 0x80000009:
2421 case 0x8000000A:
2422 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2423 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2424 setModified(IsModified_MachineData);
2425 mHWData.backup();
2426 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2427 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2428 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2429 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2430 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2431 break;
2432
2433 default:
2434 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2435 }
2436 return S_OK;
2437}
2438
2439STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2440{
2441 AutoCaller autoCaller(this);
2442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2443
2444 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2445
2446 HRESULT rc = checkStateDependency(MutableStateDep);
2447 if (FAILED(rc)) return rc;
2448
2449 switch(aId)
2450 {
2451 case 0x0:
2452 case 0x1:
2453 case 0x2:
2454 case 0x3:
2455 case 0x4:
2456 case 0x5:
2457 case 0x6:
2458 case 0x7:
2459 case 0x8:
2460 case 0x9:
2461 case 0xA:
2462 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2463 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2464 setModified(IsModified_MachineData);
2465 mHWData.backup();
2466 /* Invalidate leaf. */
2467 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2468 break;
2469
2470 case 0x80000000:
2471 case 0x80000001:
2472 case 0x80000002:
2473 case 0x80000003:
2474 case 0x80000004:
2475 case 0x80000005:
2476 case 0x80000006:
2477 case 0x80000007:
2478 case 0x80000008:
2479 case 0x80000009:
2480 case 0x8000000A:
2481 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2482 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2483 setModified(IsModified_MachineData);
2484 mHWData.backup();
2485 /* Invalidate leaf. */
2486 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2487 break;
2488
2489 default:
2490 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2491 }
2492 return S_OK;
2493}
2494
2495STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2496{
2497 AutoCaller autoCaller(this);
2498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2499
2500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2501
2502 HRESULT rc = checkStateDependency(MutableStateDep);
2503 if (FAILED(rc)) return rc;
2504
2505 setModified(IsModified_MachineData);
2506 mHWData.backup();
2507
2508 /* Invalidate all standard leafs. */
2509 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2510 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2511
2512 /* Invalidate all extended leafs. */
2513 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2514 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2515
2516 return S_OK;
2517}
2518
2519STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2520{
2521 CheckComArgOutPointerValid(aVal);
2522
2523 AutoCaller autoCaller(this);
2524 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2525
2526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2527
2528 switch(property)
2529 {
2530 case HWVirtExPropertyType_Enabled:
2531 *aVal = mHWData->mHWVirtExEnabled;
2532 break;
2533
2534 case HWVirtExPropertyType_Exclusive:
2535 *aVal = mHWData->mHWVirtExExclusive;
2536 break;
2537
2538 case HWVirtExPropertyType_VPID:
2539 *aVal = mHWData->mHWVirtExVPIDEnabled;
2540 break;
2541
2542 case HWVirtExPropertyType_NestedPaging:
2543 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2544 break;
2545
2546 case HWVirtExPropertyType_UnrestrictedExecution:
2547 *aVal = mHWData->mHWVirtExUXEnabled;
2548 break;
2549
2550 case HWVirtExPropertyType_LargePages:
2551 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2552#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2553 *aVal = FALSE;
2554#endif
2555 break;
2556
2557 case HWVirtExPropertyType_Force:
2558 *aVal = mHWData->mHWVirtExForceEnabled;
2559 break;
2560
2561 default:
2562 return E_INVALIDARG;
2563 }
2564 return S_OK;
2565}
2566
2567STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2568{
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 HRESULT rc = checkStateDependency(MutableStateDep);
2575 if (FAILED(rc)) return rc;
2576
2577 switch(property)
2578 {
2579 case HWVirtExPropertyType_Enabled:
2580 setModified(IsModified_MachineData);
2581 mHWData.backup();
2582 mHWData->mHWVirtExEnabled = !!aVal;
2583 break;
2584
2585 case HWVirtExPropertyType_Exclusive:
2586 setModified(IsModified_MachineData);
2587 mHWData.backup();
2588 mHWData->mHWVirtExExclusive = !!aVal;
2589 break;
2590
2591 case HWVirtExPropertyType_VPID:
2592 setModified(IsModified_MachineData);
2593 mHWData.backup();
2594 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2595 break;
2596
2597 case HWVirtExPropertyType_NestedPaging:
2598 setModified(IsModified_MachineData);
2599 mHWData.backup();
2600 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2601 break;
2602
2603 case HWVirtExPropertyType_UnrestrictedExecution:
2604 setModified(IsModified_MachineData);
2605 mHWData.backup();
2606 mHWData->mHWVirtExUXEnabled = !!aVal;
2607 break;
2608
2609 case HWVirtExPropertyType_LargePages:
2610 setModified(IsModified_MachineData);
2611 mHWData.backup();
2612 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2613 break;
2614
2615 case HWVirtExPropertyType_Force:
2616 setModified(IsModified_MachineData);
2617 mHWData.backup();
2618 mHWData->mHWVirtExForceEnabled = !!aVal;
2619 break;
2620
2621 default:
2622 return E_INVALIDARG;
2623 }
2624
2625 return S_OK;
2626}
2627
2628STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2629{
2630 CheckComArgOutPointerValid(aSnapshotFolder);
2631
2632 AutoCaller autoCaller(this);
2633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2634
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 Utf8Str strFullSnapshotFolder;
2638 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2639 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2640
2641 return S_OK;
2642}
2643
2644STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2645{
2646 /* @todo (r=dmik):
2647 * 1. Allow to change the name of the snapshot folder containing snapshots
2648 * 2. Rename the folder on disk instead of just changing the property
2649 * value (to be smart and not to leave garbage). Note that it cannot be
2650 * done here because the change may be rolled back. Thus, the right
2651 * place is #saveSettings().
2652 */
2653
2654 AutoCaller autoCaller(this);
2655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2656
2657 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 HRESULT rc = checkStateDependency(MutableStateDep);
2660 if (FAILED(rc)) return rc;
2661
2662 if (!mData->mCurrentSnapshot.isNull())
2663 return setError(E_FAIL,
2664 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2665
2666 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2667
2668 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2669 if (strSnapshotFolder.isEmpty())
2670 strSnapshotFolder = "Snapshots";
2671 int vrc = calculateFullPath(strSnapshotFolder,
2672 strSnapshotFolder);
2673 if (RT_FAILURE(vrc))
2674 return setError(E_FAIL,
2675 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2676 aSnapshotFolder, vrc);
2677
2678 setModified(IsModified_MachineData);
2679 mUserData.backup();
2680
2681 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2682
2683 return S_OK;
2684}
2685
2686STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2687{
2688 CheckComArgOutSafeArrayPointerValid(aAttachments);
2689
2690 AutoCaller autoCaller(this);
2691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2692
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2696 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2697
2698 return S_OK;
2699}
2700
2701STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2702{
2703 CheckComArgOutPointerValid(vrdeServer);
2704
2705 AutoCaller autoCaller(this);
2706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2707
2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2709
2710 Assert(!!mVRDEServer);
2711 mVRDEServer.queryInterfaceTo(vrdeServer);
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2717{
2718 CheckComArgOutPointerValid(audioAdapter);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 mAudioAdapter.queryInterfaceTo(audioAdapter);
2726 return S_OK;
2727}
2728
2729STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2730{
2731#ifdef VBOX_WITH_VUSB
2732 CheckComArgOutPointerValid(aUSBController);
2733
2734 AutoCaller autoCaller(this);
2735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2736
2737 clearError();
2738 MultiResult rc(S_OK);
2739
2740# ifdef VBOX_WITH_USB
2741 rc = mParent->host()->checkUSBProxyService();
2742 if (FAILED(rc)) return rc;
2743# endif
2744
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 return rc = mUSBController.queryInterfaceTo(aUSBController);
2748#else
2749 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2750 * extended error info to indicate that USB is simply not available
2751 * (w/o treating it as a failure), for example, as in OSE */
2752 NOREF(aUSBController);
2753 ReturnComNotImplemented();
2754#endif /* VBOX_WITH_VUSB */
2755}
2756
2757STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2758{
2759 CheckComArgOutPointerValid(aFilePath);
2760
2761 AutoLimitedCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 mData->m_strConfigFileFull.cloneTo(aFilePath);
2767 return S_OK;
2768}
2769
2770STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2771{
2772 CheckComArgOutPointerValid(aModified);
2773
2774 AutoCaller autoCaller(this);
2775 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2776
2777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2778
2779 HRESULT rc = checkStateDependency(MutableStateDep);
2780 if (FAILED(rc)) return rc;
2781
2782 if (!mData->pMachineConfigFile->fileExists())
2783 // this is a new machine, and no config file exists yet:
2784 *aModified = TRUE;
2785 else
2786 *aModified = (mData->flModifications != 0);
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2792{
2793 CheckComArgOutPointerValid(aSessionState);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aSessionState = mData->mSession.mState;
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2806{
2807 CheckComArgOutPointerValid(aSessionType);
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 mData->mSession.mType.cloneTo(aSessionType);
2815
2816 return S_OK;
2817}
2818
2819STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2820{
2821 CheckComArgOutPointerValid(aSessionPID);
2822
2823 AutoCaller autoCaller(this);
2824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2825
2826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2827
2828 *aSessionPID = mData->mSession.mPID;
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2834{
2835 CheckComArgOutPointerValid(machineState);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *machineState = mData->mMachineState;
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2848{
2849 CheckComArgOutPointerValid(aLastStateChange);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2862{
2863 CheckComArgOutPointerValid(aStateFilePath);
2864
2865 AutoCaller autoCaller(this);
2866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2876{
2877 CheckComArgOutPointerValid(aLogFolder);
2878
2879 AutoCaller autoCaller(this);
2880 AssertComRCReturnRC(autoCaller.rc());
2881
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 Utf8Str logFolder;
2885 getLogFolder(logFolder);
2886 logFolder.cloneTo(aLogFolder);
2887
2888 return S_OK;
2889}
2890
2891STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2892{
2893 CheckComArgOutPointerValid(aCurrentSnapshot);
2894
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2901
2902 return S_OK;
2903}
2904
2905STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2906{
2907 CheckComArgOutPointerValid(aSnapshotCount);
2908
2909 AutoCaller autoCaller(this);
2910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2911
2912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2913
2914 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2915 ? 0
2916 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2917
2918 return S_OK;
2919}
2920
2921STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2922{
2923 CheckComArgOutPointerValid(aCurrentStateModified);
2924
2925 AutoCaller autoCaller(this);
2926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2927
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929
2930 /* Note: for machines with no snapshots, we always return FALSE
2931 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2932 * reasons :) */
2933
2934 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2935 ? FALSE
2936 : mData->mCurrentStateModified;
2937
2938 return S_OK;
2939}
2940
2941STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2942{
2943 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2944
2945 AutoCaller autoCaller(this);
2946 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2947
2948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2949
2950 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2951 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2952
2953 return S_OK;
2954}
2955
2956STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2957{
2958 CheckComArgOutPointerValid(aClipboardMode);
2959
2960 AutoCaller autoCaller(this);
2961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2962
2963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2964
2965 *aClipboardMode = mHWData->mClipboardMode;
2966
2967 return S_OK;
2968}
2969
2970STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2971{
2972 HRESULT rc = S_OK;
2973
2974 AutoCaller autoCaller(this);
2975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2976
2977 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2978
2979 alock.release();
2980 rc = onClipboardModeChange(aClipboardMode);
2981 alock.acquire();
2982 if (FAILED(rc)) return rc;
2983
2984 setModified(IsModified_MachineData);
2985 mHWData.backup();
2986 mHWData->mClipboardMode = aClipboardMode;
2987
2988 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2989 if (Global::IsOnline(mData->mMachineState))
2990 saveSettings(NULL);
2991
2992 return S_OK;
2993}
2994
2995STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2996{
2997 CheckComArgOutPointerValid(aDragAndDropMode);
2998
2999 AutoCaller autoCaller(this);
3000 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3001
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 *aDragAndDropMode = mHWData->mDragAndDropMode;
3005
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3010{
3011 HRESULT rc = S_OK;
3012
3013 AutoCaller autoCaller(this);
3014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3015
3016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 alock.release();
3019 rc = onDragAndDropModeChange(aDragAndDropMode);
3020 alock.acquire();
3021 if (FAILED(rc)) return rc;
3022
3023 setModified(IsModified_MachineData);
3024 mHWData.backup();
3025 mHWData->mDragAndDropMode = aDragAndDropMode;
3026
3027 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3028 if (Global::IsOnline(mData->mMachineState))
3029 saveSettings(NULL);
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3035{
3036 CheckComArgOutPointerValid(aPatterns);
3037
3038 AutoCaller autoCaller(this);
3039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3040
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 try
3044 {
3045 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3046 }
3047 catch (...)
3048 {
3049 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3050 }
3051
3052 return S_OK;
3053}
3054
3055STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3056{
3057 AutoCaller autoCaller(this);
3058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3059
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061
3062 HRESULT rc = checkStateDependency(MutableStateDep);
3063 if (FAILED(rc)) return rc;
3064
3065 setModified(IsModified_MachineData);
3066 mHWData.backup();
3067 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3068 return rc;
3069}
3070
3071STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3072{
3073 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3074
3075 AutoCaller autoCaller(this);
3076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3077
3078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3081 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3087{
3088 CheckComArgOutPointerValid(aEnabled);
3089
3090 AutoCaller autoCaller(this);
3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3092
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 *aEnabled = mUserData->s.fTeleporterEnabled;
3096
3097 return S_OK;
3098}
3099
3100STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3101{
3102 AutoCaller autoCaller(this);
3103 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3104
3105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 /* Only allow it to be set to true when PoweredOff or Aborted.
3108 (Clearing it is always permitted.) */
3109 if ( aEnabled
3110 && mData->mRegistered
3111 && ( !isSessionMachine()
3112 || ( mData->mMachineState != MachineState_PoweredOff
3113 && mData->mMachineState != MachineState_Teleported
3114 && mData->mMachineState != MachineState_Aborted
3115 )
3116 )
3117 )
3118 return setError(VBOX_E_INVALID_VM_STATE,
3119 tr("The machine is not powered off (state is %s)"),
3120 Global::stringifyMachineState(mData->mMachineState));
3121
3122 setModified(IsModified_MachineData);
3123 mUserData.backup();
3124 mUserData->s.fTeleporterEnabled = !!aEnabled;
3125
3126 return S_OK;
3127}
3128
3129STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3130{
3131 CheckComArgOutPointerValid(aPort);
3132
3133 AutoCaller autoCaller(this);
3134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3135
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3139
3140 return S_OK;
3141}
3142
3143STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3144{
3145 if (aPort >= _64K)
3146 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3147
3148 AutoCaller autoCaller(this);
3149 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3150
3151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 HRESULT rc = checkStateDependency(MutableStateDep);
3154 if (FAILED(rc)) return rc;
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3164{
3165 CheckComArgOutPointerValid(aAddress);
3166
3167 AutoCaller autoCaller(this);
3168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3169
3170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3173
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3178{
3179 AutoCaller autoCaller(this);
3180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3181
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 HRESULT rc = checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 setModified(IsModified_MachineData);
3188 mUserData.backup();
3189 mUserData->s.strTeleporterAddress = aAddress;
3190
3191 return S_OK;
3192}
3193
3194STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3195{
3196 CheckComArgOutPointerValid(aPassword);
3197
3198 AutoCaller autoCaller(this);
3199 HRESULT hrc = autoCaller.rc();
3200 if (SUCCEEDED(hrc))
3201 {
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3204 }
3205
3206 return hrc;
3207}
3208
3209STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3210{
3211 /*
3212 * Hash the password first.
3213 */
3214 Utf8Str strPassword(aPassword);
3215 if (!strPassword.isEmpty())
3216 {
3217 if (VBoxIsPasswordHashed(&strPassword))
3218 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3219 VBoxHashPassword(&strPassword);
3220 }
3221
3222 /*
3223 * Do the update.
3224 */
3225 AutoCaller autoCaller(this);
3226 HRESULT hrc = autoCaller.rc();
3227 if (SUCCEEDED(hrc))
3228 {
3229 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3230 hrc = checkStateDependency(MutableStateDep);
3231 if (SUCCEEDED(hrc))
3232 {
3233 setModified(IsModified_MachineData);
3234 mUserData.backup();
3235 mUserData->s.strTeleporterPassword = strPassword;
3236 }
3237 }
3238
3239 return hrc;
3240}
3241
3242STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3243{
3244 CheckComArgOutPointerValid(aState);
3245
3246 AutoCaller autoCaller(this);
3247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3248
3249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 *aState = mUserData->s.enmFaultToleranceState;
3252 return S_OK;
3253}
3254
3255STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3256{
3257 AutoCaller autoCaller(this);
3258 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3259
3260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3261
3262 /* @todo deal with running state change. */
3263 HRESULT rc = checkStateDependency(MutableStateDep);
3264 if (FAILED(rc)) return rc;
3265
3266 setModified(IsModified_MachineData);
3267 mUserData.backup();
3268 mUserData->s.enmFaultToleranceState = aState;
3269 return S_OK;
3270}
3271
3272STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3273{
3274 CheckComArgOutPointerValid(aAddress);
3275
3276 AutoCaller autoCaller(this);
3277 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3278
3279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3280
3281 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3282 return S_OK;
3283}
3284
3285STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3286{
3287 AutoCaller autoCaller(this);
3288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3289
3290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3291
3292 /* @todo deal with running state change. */
3293 HRESULT rc = checkStateDependency(MutableStateDep);
3294 if (FAILED(rc)) return rc;
3295
3296 setModified(IsModified_MachineData);
3297 mUserData.backup();
3298 mUserData->s.strFaultToleranceAddress = aAddress;
3299 return S_OK;
3300}
3301
3302STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3303{
3304 CheckComArgOutPointerValid(aPort);
3305
3306 AutoCaller autoCaller(this);
3307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3308
3309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3310
3311 *aPort = mUserData->s.uFaultTolerancePort;
3312 return S_OK;
3313}
3314
3315STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3316{
3317 AutoCaller autoCaller(this);
3318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3319
3320 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3321
3322 /* @todo deal with running state change. */
3323 HRESULT rc = checkStateDependency(MutableStateDep);
3324 if (FAILED(rc)) return rc;
3325
3326 setModified(IsModified_MachineData);
3327 mUserData.backup();
3328 mUserData->s.uFaultTolerancePort = aPort;
3329 return S_OK;
3330}
3331
3332STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3333{
3334 CheckComArgOutPointerValid(aPassword);
3335
3336 AutoCaller autoCaller(this);
3337 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3338
3339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3340
3341 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3342
3343 return S_OK;
3344}
3345
3346STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3347{
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3352
3353 /* @todo deal with running state change. */
3354 HRESULT rc = checkStateDependency(MutableStateDep);
3355 if (FAILED(rc)) return rc;
3356
3357 setModified(IsModified_MachineData);
3358 mUserData.backup();
3359 mUserData->s.strFaultTolerancePassword = aPassword;
3360
3361 return S_OK;
3362}
3363
3364STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3365{
3366 CheckComArgOutPointerValid(aInterval);
3367
3368 AutoCaller autoCaller(this);
3369 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3370
3371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3372
3373 *aInterval = mUserData->s.uFaultToleranceInterval;
3374 return S_OK;
3375}
3376
3377STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3378{
3379 AutoCaller autoCaller(this);
3380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3381
3382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3383
3384 /* @todo deal with running state change. */
3385 HRESULT rc = checkStateDependency(MutableStateDep);
3386 if (FAILED(rc)) return rc;
3387
3388 setModified(IsModified_MachineData);
3389 mUserData.backup();
3390 mUserData->s.uFaultToleranceInterval = aInterval;
3391 return S_OK;
3392}
3393
3394STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3395{
3396 CheckComArgOutPointerValid(aEnabled);
3397
3398 AutoCaller autoCaller(this);
3399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3400
3401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3402
3403 *aEnabled = mUserData->s.fRTCUseUTC;
3404
3405 return S_OK;
3406}
3407
3408STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3409{
3410 AutoCaller autoCaller(this);
3411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3412
3413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3414
3415 /* Only allow it to be set to true when PoweredOff or Aborted.
3416 (Clearing it is always permitted.) */
3417 if ( aEnabled
3418 && mData->mRegistered
3419 && ( !isSessionMachine()
3420 || ( mData->mMachineState != MachineState_PoweredOff
3421 && mData->mMachineState != MachineState_Teleported
3422 && mData->mMachineState != MachineState_Aborted
3423 )
3424 )
3425 )
3426 return setError(VBOX_E_INVALID_VM_STATE,
3427 tr("The machine is not powered off (state is %s)"),
3428 Global::stringifyMachineState(mData->mMachineState));
3429
3430 setModified(IsModified_MachineData);
3431 mUserData.backup();
3432 mUserData->s.fRTCUseUTC = !!aEnabled;
3433
3434 return S_OK;
3435}
3436
3437STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3438{
3439 CheckComArgOutPointerValid(aEnabled);
3440
3441 AutoCaller autoCaller(this);
3442 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3443
3444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3445
3446 *aEnabled = mHWData->mIOCacheEnabled;
3447
3448 return S_OK;
3449}
3450
3451STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3452{
3453 AutoCaller autoCaller(this);
3454 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3455
3456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3457
3458 HRESULT rc = checkStateDependency(MutableStateDep);
3459 if (FAILED(rc)) return rc;
3460
3461 setModified(IsModified_MachineData);
3462 mHWData.backup();
3463 mHWData->mIOCacheEnabled = aEnabled;
3464
3465 return S_OK;
3466}
3467
3468STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3469{
3470 CheckComArgOutPointerValid(aIOCacheSize);
3471
3472 AutoCaller autoCaller(this);
3473 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3474
3475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3476
3477 *aIOCacheSize = mHWData->mIOCacheSize;
3478
3479 return S_OK;
3480}
3481
3482STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3483{
3484 AutoCaller autoCaller(this);
3485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mIOCacheSize = aIOCacheSize;
3495
3496 return S_OK;
3497}
3498
3499
3500/**
3501 * @note Locks objects!
3502 */
3503STDMETHODIMP Machine::LockMachine(ISession *aSession,
3504 LockType_T lockType)
3505{
3506 CheckComArgNotNull(aSession);
3507
3508 AutoCaller autoCaller(this);
3509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3510
3511 /* check the session state */
3512 SessionState_T state;
3513 HRESULT rc = aSession->COMGETTER(State)(&state);
3514 if (FAILED(rc)) return rc;
3515
3516 if (state != SessionState_Unlocked)
3517 return setError(VBOX_E_INVALID_OBJECT_STATE,
3518 tr("The given session is busy"));
3519
3520 // get the client's IInternalSessionControl interface
3521 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3522 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3523 E_INVALIDARG);
3524
3525 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3526
3527 if (!mData->mRegistered)
3528 return setError(E_UNEXPECTED,
3529 tr("The machine '%s' is not registered"),
3530 mUserData->s.strName.c_str());
3531
3532 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3533
3534 SessionState_T oldState = mData->mSession.mState;
3535 /* Hack: in case the session is closing and there is a progress object
3536 * which allows waiting for the session to be closed, take the opportunity
3537 * and do a limited wait (max. 1 second). This helps a lot when the system
3538 * is busy and thus session closing can take a little while. */
3539 if ( mData->mSession.mState == SessionState_Unlocking
3540 && mData->mSession.mProgress)
3541 {
3542 alock.release();
3543 mData->mSession.mProgress->WaitForCompletion(1000);
3544 alock.acquire();
3545 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3546 }
3547
3548 // try again now
3549 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3550 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3551 )
3552 {
3553 // OK, share the session... we are now dealing with three processes:
3554 // 1) VBoxSVC (where this code runs);
3555 // 2) process C: the caller's client process (who wants a shared session);
3556 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3557
3558 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3559 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3560 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3561 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3562 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3563
3564 /*
3565 * Release the lock before calling the client process. It's safe here
3566 * since the only thing to do after we get the lock again is to add
3567 * the remote control to the list (which doesn't directly influence
3568 * anything).
3569 */
3570 alock.release();
3571
3572 // get the console of the session holding the write lock (this is a remote call)
3573 ComPtr<IConsole> pConsoleW;
3574 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3575 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3576 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3577 if (FAILED(rc))
3578 // the failure may occur w/o any error info (from RPC), so provide one
3579 return setError(VBOX_E_VM_ERROR,
3580 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3581
3582 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3583
3584 // share the session machine and W's console with the caller's session
3585 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3586 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3587 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3588
3589 if (FAILED(rc))
3590 // the failure may occur w/o any error info (from RPC), so provide one
3591 return setError(VBOX_E_VM_ERROR,
3592 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3593 alock.acquire();
3594
3595 // need to revalidate the state after acquiring the lock again
3596 if (mData->mSession.mState != SessionState_Locked)
3597 {
3598 pSessionControl->Uninitialize();
3599 return setError(VBOX_E_INVALID_SESSION_STATE,
3600 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3601 mUserData->s.strName.c_str());
3602 }
3603
3604 // add the caller's session to the list
3605 mData->mSession.mRemoteControls.push_back(pSessionControl);
3606 }
3607 else if ( mData->mSession.mState == SessionState_Locked
3608 || mData->mSession.mState == SessionState_Unlocking
3609 )
3610 {
3611 // sharing not permitted, or machine still unlocking:
3612 return setError(VBOX_E_INVALID_OBJECT_STATE,
3613 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3614 mUserData->s.strName.c_str());
3615 }
3616 else
3617 {
3618 // machine is not locked: then write-lock the machine (create the session machine)
3619
3620 // must not be busy
3621 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3622
3623 // get the caller's session PID
3624 RTPROCESS pid = NIL_RTPROCESS;
3625 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3626 pSessionControl->GetPID((ULONG*)&pid);
3627 Assert(pid != NIL_RTPROCESS);
3628
3629 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3630
3631 if (fLaunchingVMProcess)
3632 {
3633 // this machine is awaiting for a spawning session to be opened:
3634 // then the calling process must be the one that got started by
3635 // LaunchVMProcess()
3636
3637 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3638 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3639
3640 if (mData->mSession.mPID != pid)
3641 return setError(E_ACCESSDENIED,
3642 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3643 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3644 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3645 }
3646
3647 // create the mutable SessionMachine from the current machine
3648 ComObjPtr<SessionMachine> sessionMachine;
3649 sessionMachine.createObject();
3650 rc = sessionMachine->init(this);
3651 AssertComRC(rc);
3652
3653 /* NOTE: doing return from this function after this point but
3654 * before the end is forbidden since it may call SessionMachine::uninit()
3655 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3656 * lock while still holding the Machine lock in alock so that a deadlock
3657 * is possible due to the wrong lock order. */
3658
3659 if (SUCCEEDED(rc))
3660 {
3661 /*
3662 * Set the session state to Spawning to protect against subsequent
3663 * attempts to open a session and to unregister the machine after
3664 * we release the lock.
3665 */
3666 SessionState_T origState = mData->mSession.mState;
3667 mData->mSession.mState = SessionState_Spawning;
3668
3669 /*
3670 * Release the lock before calling the client process -- it will call
3671 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3672 * because the state is Spawning, so that LaunchVMProcess() and
3673 * LockMachine() calls will fail. This method, called before we
3674 * acquire the lock again, will fail because of the wrong PID.
3675 *
3676 * Note that mData->mSession.mRemoteControls accessed outside
3677 * the lock may not be modified when state is Spawning, so it's safe.
3678 */
3679 alock.release();
3680
3681 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3682 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3683 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3684
3685 /* The failure may occur w/o any error info (from RPC), so provide one */
3686 if (FAILED(rc))
3687 setError(VBOX_E_VM_ERROR,
3688 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3689
3690 if ( SUCCEEDED(rc)
3691 && fLaunchingVMProcess
3692 )
3693 {
3694 /* complete the remote session initialization */
3695
3696 /* get the console from the direct session */
3697 ComPtr<IConsole> console;
3698 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3699 ComAssertComRC(rc);
3700
3701 if (SUCCEEDED(rc) && !console)
3702 {
3703 ComAssert(!!console);
3704 rc = E_FAIL;
3705 }
3706
3707 /* assign machine & console to the remote session */
3708 if (SUCCEEDED(rc))
3709 {
3710 /*
3711 * after LaunchVMProcess(), the first and the only
3712 * entry in remoteControls is that remote session
3713 */
3714 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3715 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3716 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3717
3718 /* The failure may occur w/o any error info (from RPC), so provide one */
3719 if (FAILED(rc))
3720 setError(VBOX_E_VM_ERROR,
3721 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3722 }
3723
3724 if (FAILED(rc))
3725 pSessionControl->Uninitialize();
3726 }
3727
3728 /* acquire the lock again */
3729 alock.acquire();
3730
3731 /* Restore the session state */
3732 mData->mSession.mState = origState;
3733 }
3734
3735 // finalize spawning anyway (this is why we don't return on errors above)
3736 if (fLaunchingVMProcess)
3737 {
3738 /* Note that the progress object is finalized later */
3739 /** @todo Consider checking mData->mSession.mProgress for cancellation
3740 * around here. */
3741
3742 /* We don't reset mSession.mPID here because it is necessary for
3743 * SessionMachine::uninit() to reap the child process later. */
3744
3745 if (FAILED(rc))
3746 {
3747 /* Close the remote session, remove the remote control from the list
3748 * and reset session state to Closed (@note keep the code in sync
3749 * with the relevant part in openSession()). */
3750
3751 Assert(mData->mSession.mRemoteControls.size() == 1);
3752 if (mData->mSession.mRemoteControls.size() == 1)
3753 {
3754 ErrorInfoKeeper eik;
3755 mData->mSession.mRemoteControls.front()->Uninitialize();
3756 }
3757
3758 mData->mSession.mRemoteControls.clear();
3759 mData->mSession.mState = SessionState_Unlocked;
3760 }
3761 }
3762 else
3763 {
3764 /* memorize PID of the directly opened session */
3765 if (SUCCEEDED(rc))
3766 mData->mSession.mPID = pid;
3767 }
3768
3769 if (SUCCEEDED(rc))
3770 {
3771 /* memorize the direct session control and cache IUnknown for it */
3772 mData->mSession.mDirectControl = pSessionControl;
3773 mData->mSession.mState = SessionState_Locked;
3774 /* associate the SessionMachine with this Machine */
3775 mData->mSession.mMachine = sessionMachine;
3776
3777 /* request an IUnknown pointer early from the remote party for later
3778 * identity checks (it will be internally cached within mDirectControl
3779 * at least on XPCOM) */
3780 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3781 NOREF(unk);
3782 }
3783
3784 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3785 * would break the lock order */
3786 alock.release();
3787
3788 /* uninitialize the created session machine on failure */
3789 if (FAILED(rc))
3790 sessionMachine->uninit();
3791
3792 }
3793
3794 if (SUCCEEDED(rc))
3795 {
3796 /*
3797 * tell the client watcher thread to update the set of
3798 * machines that have open sessions
3799 */
3800 mParent->updateClientWatcher();
3801
3802 if (oldState != SessionState_Locked)
3803 /* fire an event */
3804 mParent->onSessionStateChange(getId(), SessionState_Locked);
3805 }
3806
3807 return rc;
3808}
3809
3810/**
3811 * @note Locks objects!
3812 */
3813STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3814 IN_BSTR aFrontend,
3815 IN_BSTR aEnvironment,
3816 IProgress **aProgress)
3817{
3818 CheckComArgStr(aFrontend);
3819 Utf8Str strFrontend(aFrontend);
3820 Utf8Str strEnvironment(aEnvironment);
3821 /* "emergencystop" doesn't need the session, so skip the checks/interface
3822 * retrieval. This code doesn't quite fit in here, but introducing a
3823 * special API method would be even more effort, and would require explicit
3824 * support by every API client. It's better to hide the feature a bit. */
3825 if (strFrontend != "emergencystop")
3826 CheckComArgNotNull(aSession);
3827 CheckComArgOutPointerValid(aProgress);
3828
3829 AutoCaller autoCaller(this);
3830 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3831
3832 HRESULT rc = S_OK;
3833 if (strFrontend.isEmpty())
3834 {
3835 Bstr bstrFrontend;
3836 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3837 if (FAILED(rc))
3838 return rc;
3839 strFrontend = bstrFrontend;
3840 if (strFrontend.isEmpty())
3841 {
3842 ComPtr<ISystemProperties> systemProperties;
3843 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3844 if (FAILED(rc))
3845 return rc;
3846 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3847 if (FAILED(rc))
3848 return rc;
3849 strFrontend = bstrFrontend;
3850 }
3851 /* paranoia - emergencystop is not a valid default */
3852 if (strFrontend == "emergencystop")
3853 strFrontend = Utf8Str::Empty;
3854 }
3855 /* default frontend: Qt GUI */
3856 if (strFrontend.isEmpty())
3857 strFrontend = "GUI/Qt";
3858
3859 if (strFrontend != "emergencystop")
3860 {
3861 /* check the session state */
3862 SessionState_T state;
3863 rc = aSession->COMGETTER(State)(&state);
3864 if (FAILED(rc))
3865 return rc;
3866
3867 if (state != SessionState_Unlocked)
3868 return setError(VBOX_E_INVALID_OBJECT_STATE,
3869 tr("The given session is busy"));
3870
3871 /* get the IInternalSessionControl interface */
3872 ComPtr<IInternalSessionControl> control(aSession);
3873 ComAssertMsgRet(!control.isNull(),
3874 ("No IInternalSessionControl interface"),
3875 E_INVALIDARG);
3876
3877 /* get the teleporter enable state for the progress object init. */
3878 BOOL fTeleporterEnabled;
3879 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3880 if (FAILED(rc))
3881 return rc;
3882
3883 /* create a progress object */
3884 ComObjPtr<ProgressProxy> progress;
3885 progress.createObject();
3886 rc = progress->init(mParent,
3887 static_cast<IMachine*>(this),
3888 Bstr(tr("Starting VM")).raw(),
3889 TRUE /* aCancelable */,
3890 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3891 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3892 2 /* uFirstOperationWeight */,
3893 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3894
3895 if (SUCCEEDED(rc))
3896 {
3897 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3898 if (SUCCEEDED(rc))
3899 {
3900 progress.queryInterfaceTo(aProgress);
3901
3902 /* signal the client watcher thread */
3903 mParent->updateClientWatcher();
3904
3905 /* fire an event */
3906 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3907 }
3908 }
3909 }
3910 else
3911 {
3912 /* no progress object - either instant success or failure */
3913 *aProgress = NULL;
3914
3915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3916
3917 if (mData->mSession.mState != SessionState_Locked)
3918 return setError(VBOX_E_INVALID_OBJECT_STATE,
3919 tr("The machine '%s' is not locked by a session"),
3920 mUserData->s.strName.c_str());
3921
3922 /* must have a VM process associated - do not kill normal API clients
3923 * with an open session */
3924 if (!Global::IsOnline(mData->mMachineState))
3925 return setError(VBOX_E_INVALID_OBJECT_STATE,
3926 tr("The machine '%s' does not have a VM process"),
3927 mUserData->s.strName.c_str());
3928
3929 /* forcibly terminate the VM process */
3930 if (mData->mSession.mPID != NIL_RTPROCESS)
3931 RTProcTerminate(mData->mSession.mPID);
3932
3933 /* signal the client watcher thread, as most likely the client has
3934 * been terminated */
3935 mParent->updateClientWatcher();
3936 }
3937
3938 return rc;
3939}
3940
3941STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3942{
3943 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3944 return setError(E_INVALIDARG,
3945 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3946 aPosition, SchemaDefs::MaxBootPosition);
3947
3948 if (aDevice == DeviceType_USB)
3949 return setError(E_NOTIMPL,
3950 tr("Booting from USB device is currently not supported"));
3951
3952 AutoCaller autoCaller(this);
3953 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3954
3955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3956
3957 HRESULT rc = checkStateDependency(MutableStateDep);
3958 if (FAILED(rc)) return rc;
3959
3960 setModified(IsModified_MachineData);
3961 mHWData.backup();
3962 mHWData->mBootOrder[aPosition - 1] = aDevice;
3963
3964 return S_OK;
3965}
3966
3967STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3968{
3969 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3970 return setError(E_INVALIDARG,
3971 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3972 aPosition, SchemaDefs::MaxBootPosition);
3973
3974 AutoCaller autoCaller(this);
3975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3976
3977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3978
3979 *aDevice = mHWData->mBootOrder[aPosition - 1];
3980
3981 return S_OK;
3982}
3983
3984STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3985 LONG aControllerPort,
3986 LONG aDevice,
3987 DeviceType_T aType,
3988 IMedium *aMedium)
3989{
3990 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3991 aControllerName, aControllerPort, aDevice, aType, aMedium));
3992
3993 CheckComArgStrNotEmptyOrNull(aControllerName);
3994
3995 AutoCaller autoCaller(this);
3996 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3997
3998 // request the host lock first, since might be calling Host methods for getting host drives;
3999 // next, protect the media tree all the while we're in here, as well as our member variables
4000 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4001 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4002
4003 HRESULT rc = checkStateDependency(MutableStateDep);
4004 if (FAILED(rc)) return rc;
4005
4006 /// @todo NEWMEDIA implicit machine registration
4007 if (!mData->mRegistered)
4008 return setError(VBOX_E_INVALID_OBJECT_STATE,
4009 tr("Cannot attach storage devices to an unregistered machine"));
4010
4011 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4012
4013 /* Check for an existing controller. */
4014 ComObjPtr<StorageController> ctl;
4015 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4016 if (FAILED(rc)) return rc;
4017
4018 StorageControllerType_T ctrlType;
4019 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4020 if (FAILED(rc))
4021 return setError(E_FAIL,
4022 tr("Could not get type of controller '%ls'"),
4023 aControllerName);
4024
4025 bool fSilent = false;
4026 Utf8Str strReconfig;
4027
4028 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4029 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4030 if (FAILED(rc))
4031 return rc;
4032 if ( mData->mMachineState == MachineState_Paused
4033 && strReconfig == "1")
4034 fSilent = true;
4035
4036 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4037 bool fHotplug = false;
4038 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4039 fHotplug = true;
4040
4041 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4042 return setError(VBOX_E_INVALID_VM_STATE,
4043 tr("Controller '%ls' does not support hotplugging"),
4044 aControllerName);
4045
4046 // check that the port and device are not out of range
4047 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4048 if (FAILED(rc)) return rc;
4049
4050 /* check if the device slot is already busy */
4051 MediumAttachment *pAttachTemp;
4052 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4053 aControllerName,
4054 aControllerPort,
4055 aDevice)))
4056 {
4057 Medium *pMedium = pAttachTemp->getMedium();
4058 if (pMedium)
4059 {
4060 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4061 return setError(VBOX_E_OBJECT_IN_USE,
4062 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4063 pMedium->getLocationFull().c_str(),
4064 aControllerPort,
4065 aDevice,
4066 aControllerName);
4067 }
4068 else
4069 return setError(VBOX_E_OBJECT_IN_USE,
4070 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4071 aControllerPort, aDevice, aControllerName);
4072 }
4073
4074 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4075 if (aMedium && medium.isNull())
4076 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4077
4078 AutoCaller mediumCaller(medium);
4079 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4080
4081 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4082
4083 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4084 && !medium.isNull()
4085 )
4086 return setError(VBOX_E_OBJECT_IN_USE,
4087 tr("Medium '%s' is already attached to this virtual machine"),
4088 medium->getLocationFull().c_str());
4089
4090 if (!medium.isNull())
4091 {
4092 MediumType_T mtype = medium->getType();
4093 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4094 // For DVDs it's not written to the config file, so needs no global config
4095 // version bump. For floppies it's a new attribute "type", which is ignored
4096 // by older VirtualBox version, so needs no global config version bump either.
4097 // For hard disks this type is not accepted.
4098 if (mtype == MediumType_MultiAttach)
4099 {
4100 // This type is new with VirtualBox 4.0 and therefore requires settings
4101 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4102 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4103 // two reasons: The medium type is a property of the media registry tree, which
4104 // can reside in the global config file (for pre-4.0 media); we would therefore
4105 // possibly need to bump the global config version. We don't want to do that though
4106 // because that might make downgrading to pre-4.0 impossible.
4107 // As a result, we can only use these two new types if the medium is NOT in the
4108 // global registry:
4109 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4110 if ( medium->isInRegistry(uuidGlobalRegistry)
4111 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4112 )
4113 return setError(VBOX_E_INVALID_OBJECT_STATE,
4114 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4115 "to machines that were created with VirtualBox 4.0 or later"),
4116 medium->getLocationFull().c_str());
4117 }
4118 }
4119
4120 bool fIndirect = false;
4121 if (!medium.isNull())
4122 fIndirect = medium->isReadOnly();
4123 bool associate = true;
4124
4125 do
4126 {
4127 if ( aType == DeviceType_HardDisk
4128 && mMediaData.isBackedUp())
4129 {
4130 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4131
4132 /* check if the medium was attached to the VM before we started
4133 * changing attachments in which case the attachment just needs to
4134 * be restored */
4135 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4136 {
4137 AssertReturn(!fIndirect, E_FAIL);
4138
4139 /* see if it's the same bus/channel/device */
4140 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4141 {
4142 /* the simplest case: restore the whole attachment
4143 * and return, nothing else to do */
4144 mMediaData->mAttachments.push_back(pAttachTemp);
4145
4146 /* Reattach the medium to the VM. */
4147 if (fHotplug || fSilent)
4148 {
4149 mediumLock.release();
4150 treeLock.release();
4151 alock.release();
4152
4153 MediumLockList *pMediumLockList(new MediumLockList());
4154
4155 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4156 true /* fMediumLockWrite */,
4157 NULL,
4158 *pMediumLockList);
4159 alock.acquire();
4160 if (FAILED(rc))
4161 delete pMediumLockList;
4162 else
4163 {
4164 mData->mSession.mLockedMedia.Unlock();
4165 alock.release();
4166 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4167 mData->mSession.mLockedMedia.Lock();
4168 alock.acquire();
4169 }
4170 alock.release();
4171
4172 if (SUCCEEDED(rc))
4173 {
4174 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4175 /* Remove lock list in case of error. */
4176 if (FAILED(rc))
4177 {
4178 mData->mSession.mLockedMedia.Unlock();
4179 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4180 mData->mSession.mLockedMedia.Lock();
4181 }
4182 }
4183 }
4184
4185 return S_OK;
4186 }
4187
4188 /* bus/channel/device differ; we need a new attachment object,
4189 * but don't try to associate it again */
4190 associate = false;
4191 break;
4192 }
4193 }
4194
4195 /* go further only if the attachment is to be indirect */
4196 if (!fIndirect)
4197 break;
4198
4199 /* perform the so called smart attachment logic for indirect
4200 * attachments. Note that smart attachment is only applicable to base
4201 * hard disks. */
4202
4203 if (medium->getParent().isNull())
4204 {
4205 /* first, investigate the backup copy of the current hard disk
4206 * attachments to make it possible to re-attach existing diffs to
4207 * another device slot w/o losing their contents */
4208 if (mMediaData.isBackedUp())
4209 {
4210 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4211
4212 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4213 uint32_t foundLevel = 0;
4214
4215 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4216 it != oldAtts.end();
4217 ++it)
4218 {
4219 uint32_t level = 0;
4220 MediumAttachment *pAttach = *it;
4221 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4222 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4223 if (pMedium.isNull())
4224 continue;
4225
4226 if (pMedium->getBase(&level) == medium)
4227 {
4228 /* skip the hard disk if its currently attached (we
4229 * cannot attach the same hard disk twice) */
4230 if (findAttachment(mMediaData->mAttachments,
4231 pMedium))
4232 continue;
4233
4234 /* matched device, channel and bus (i.e. attached to the
4235 * same place) will win and immediately stop the search;
4236 * otherwise the attachment that has the youngest
4237 * descendant of medium will be used
4238 */
4239 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4240 {
4241 /* the simplest case: restore the whole attachment
4242 * and return, nothing else to do */
4243 mMediaData->mAttachments.push_back(*it);
4244
4245 /* Reattach the medium to the VM. */
4246 if (fHotplug || fSilent)
4247 {
4248 mediumLock.release();
4249 treeLock.release();
4250 alock.release();
4251
4252 MediumLockList *pMediumLockList(new MediumLockList());
4253
4254 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4255 true /* fMediumLockWrite */,
4256 NULL,
4257 *pMediumLockList);
4258 alock.acquire();
4259 if (FAILED(rc))
4260 delete pMediumLockList;
4261 else
4262 {
4263 mData->mSession.mLockedMedia.Unlock();
4264 alock.release();
4265 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4266 mData->mSession.mLockedMedia.Lock();
4267 alock.acquire();
4268 }
4269 alock.release();
4270
4271 if (SUCCEEDED(rc))
4272 {
4273 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4274 /* Remove lock list in case of error. */
4275 if (FAILED(rc))
4276 {
4277 mData->mSession.mLockedMedia.Unlock();
4278 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4279 mData->mSession.mLockedMedia.Lock();
4280 }
4281 }
4282 }
4283
4284 return S_OK;
4285 }
4286 else if ( foundIt == oldAtts.end()
4287 || level > foundLevel /* prefer younger */
4288 )
4289 {
4290 foundIt = it;
4291 foundLevel = level;
4292 }
4293 }
4294 }
4295
4296 if (foundIt != oldAtts.end())
4297 {
4298 /* use the previously attached hard disk */
4299 medium = (*foundIt)->getMedium();
4300 mediumCaller.attach(medium);
4301 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4302 mediumLock.attach(medium);
4303 /* not implicit, doesn't require association with this VM */
4304 fIndirect = false;
4305 associate = false;
4306 /* go right to the MediumAttachment creation */
4307 break;
4308 }
4309 }
4310
4311 /* must give up the medium lock and medium tree lock as below we
4312 * go over snapshots, which needs a lock with higher lock order. */
4313 mediumLock.release();
4314 treeLock.release();
4315
4316 /* then, search through snapshots for the best diff in the given
4317 * hard disk's chain to base the new diff on */
4318
4319 ComObjPtr<Medium> base;
4320 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4321 while (snap)
4322 {
4323 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4324
4325 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4326
4327 MediumAttachment *pAttachFound = NULL;
4328 uint32_t foundLevel = 0;
4329
4330 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4331 it != snapAtts.end();
4332 ++it)
4333 {
4334 MediumAttachment *pAttach = *it;
4335 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4336 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4337 if (pMedium.isNull())
4338 continue;
4339
4340 uint32_t level = 0;
4341 if (pMedium->getBase(&level) == medium)
4342 {
4343 /* matched device, channel and bus (i.e. attached to the
4344 * same place) will win and immediately stop the search;
4345 * otherwise the attachment that has the youngest
4346 * descendant of medium will be used
4347 */
4348 if ( pAttach->getDevice() == aDevice
4349 && pAttach->getPort() == aControllerPort
4350 && pAttach->getControllerName() == aControllerName
4351 )
4352 {
4353 pAttachFound = pAttach;
4354 break;
4355 }
4356 else if ( !pAttachFound
4357 || level > foundLevel /* prefer younger */
4358 )
4359 {
4360 pAttachFound = pAttach;
4361 foundLevel = level;
4362 }
4363 }
4364 }
4365
4366 if (pAttachFound)
4367 {
4368 base = pAttachFound->getMedium();
4369 break;
4370 }
4371
4372 snap = snap->getParent();
4373 }
4374
4375 /* re-lock medium tree and the medium, as we need it below */
4376 treeLock.acquire();
4377 mediumLock.acquire();
4378
4379 /* found a suitable diff, use it as a base */
4380 if (!base.isNull())
4381 {
4382 medium = base;
4383 mediumCaller.attach(medium);
4384 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4385 mediumLock.attach(medium);
4386 }
4387 }
4388
4389 Utf8Str strFullSnapshotFolder;
4390 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4391
4392 ComObjPtr<Medium> diff;
4393 diff.createObject();
4394 // store this diff in the same registry as the parent
4395 Guid uuidRegistryParent;
4396 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4397 {
4398 // parent image has no registry: this can happen if we're attaching a new immutable
4399 // image that has not yet been attached (medium then points to the base and we're
4400 // creating the diff image for the immutable, and the parent is not yet registered);
4401 // put the parent in the machine registry then
4402 mediumLock.release();
4403 treeLock.release();
4404 alock.release();
4405 addMediumToRegistry(medium);
4406 alock.acquire();
4407 treeLock.acquire();
4408 mediumLock.acquire();
4409 medium->getFirstRegistryMachineId(uuidRegistryParent);
4410 }
4411 rc = diff->init(mParent,
4412 medium->getPreferredDiffFormat(),
4413 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4414 uuidRegistryParent);
4415 if (FAILED(rc)) return rc;
4416
4417 /* Apply the normal locking logic to the entire chain. */
4418 MediumLockList *pMediumLockList(new MediumLockList());
4419 mediumLock.release();
4420 treeLock.release();
4421 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4422 true /* fMediumLockWrite */,
4423 medium,
4424 *pMediumLockList);
4425 treeLock.acquire();
4426 mediumLock.acquire();
4427 if (SUCCEEDED(rc))
4428 {
4429 mediumLock.release();
4430 treeLock.release();
4431 rc = pMediumLockList->Lock();
4432 treeLock.acquire();
4433 mediumLock.acquire();
4434 if (FAILED(rc))
4435 setError(rc,
4436 tr("Could not lock medium when creating diff '%s'"),
4437 diff->getLocationFull().c_str());
4438 else
4439 {
4440 /* will release the lock before the potentially lengthy
4441 * operation, so protect with the special state */
4442 MachineState_T oldState = mData->mMachineState;
4443 setMachineState(MachineState_SettingUp);
4444
4445 mediumLock.release();
4446 treeLock.release();
4447 alock.release();
4448
4449 rc = medium->createDiffStorage(diff,
4450 MediumVariant_Standard,
4451 pMediumLockList,
4452 NULL /* aProgress */,
4453 true /* aWait */);
4454
4455 alock.acquire();
4456 treeLock.acquire();
4457 mediumLock.acquire();
4458
4459 setMachineState(oldState);
4460 }
4461 }
4462
4463 /* Unlock the media and free the associated memory. */
4464 delete pMediumLockList;
4465
4466 if (FAILED(rc)) return rc;
4467
4468 /* use the created diff for the actual attachment */
4469 medium = diff;
4470 mediumCaller.attach(medium);
4471 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4472 mediumLock.attach(medium);
4473 }
4474 while (0);
4475
4476 ComObjPtr<MediumAttachment> attachment;
4477 attachment.createObject();
4478 rc = attachment->init(this,
4479 medium,
4480 aControllerName,
4481 aControllerPort,
4482 aDevice,
4483 aType,
4484 fIndirect,
4485 false /* fPassthrough */,
4486 false /* fTempEject */,
4487 false /* fNonRotational */,
4488 false /* fDiscard */,
4489 Utf8Str::Empty);
4490 if (FAILED(rc)) return rc;
4491
4492 if (associate && !medium.isNull())
4493 {
4494 // as the last step, associate the medium to the VM
4495 rc = medium->addBackReference(mData->mUuid);
4496 // here we can fail because of Deleting, or being in process of creating a Diff
4497 if (FAILED(rc)) return rc;
4498
4499 mediumLock.release();
4500 treeLock.release();
4501 alock.release();
4502 addMediumToRegistry(medium);
4503 alock.acquire();
4504 treeLock.acquire();
4505 mediumLock.acquire();
4506 }
4507
4508 /* success: finally remember the attachment */
4509 setModified(IsModified_Storage);
4510 mMediaData.backup();
4511 mMediaData->mAttachments.push_back(attachment);
4512
4513 mediumLock.release();
4514 treeLock.release();
4515 alock.release();
4516
4517 if (fHotplug || fSilent)
4518 {
4519 MediumLockList *pMediumLockList(new MediumLockList());
4520
4521 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4522 true /* fMediumLockWrite */,
4523 NULL,
4524 *pMediumLockList);
4525 alock.acquire();
4526 if (FAILED(rc))
4527 delete pMediumLockList;
4528 else
4529 {
4530 mData->mSession.mLockedMedia.Unlock();
4531 alock.release();
4532 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4533 mData->mSession.mLockedMedia.Lock();
4534 alock.acquire();
4535 }
4536 alock.release();
4537
4538 if (SUCCEEDED(rc))
4539 {
4540 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4541 /* Remove lock list in case of error. */
4542 if (FAILED(rc))
4543 {
4544 mData->mSession.mLockedMedia.Unlock();
4545 mData->mSession.mLockedMedia.Remove(attachment);
4546 mData->mSession.mLockedMedia.Lock();
4547 }
4548 }
4549 }
4550
4551 mParent->saveModifiedRegistries();
4552
4553 return rc;
4554}
4555
4556STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4557 LONG aDevice)
4558{
4559 CheckComArgStrNotEmptyOrNull(aControllerName);
4560
4561 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4562 aControllerName, aControllerPort, aDevice));
4563
4564 AutoCaller autoCaller(this);
4565 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4566
4567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4568
4569 HRESULT rc = checkStateDependency(MutableStateDep);
4570 if (FAILED(rc)) return rc;
4571
4572 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4573
4574 /* Check for an existing controller. */
4575 ComObjPtr<StorageController> ctl;
4576 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4577 if (FAILED(rc)) return rc;
4578
4579 StorageControllerType_T ctrlType;
4580 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4581 if (FAILED(rc))
4582 return setError(E_FAIL,
4583 tr("Could not get type of controller '%ls'"),
4584 aControllerName);
4585
4586 bool fSilent = false;
4587 Utf8Str strReconfig;
4588
4589 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4590 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4591 if (FAILED(rc))
4592 return rc;
4593 if ( mData->mMachineState == MachineState_Paused
4594 && strReconfig == "1")
4595 fSilent = true;
4596
4597 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4598 bool fHotplug = false;
4599 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4600 fHotplug = true;
4601
4602 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4603 return setError(VBOX_E_INVALID_VM_STATE,
4604 tr("Controller '%ls' does not support hotplugging"),
4605 aControllerName);
4606
4607 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4608 aControllerName,
4609 aControllerPort,
4610 aDevice);
4611 if (!pAttach)
4612 return setError(VBOX_E_OBJECT_NOT_FOUND,
4613 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4614 aDevice, aControllerPort, aControllerName);
4615
4616 /*
4617 * The VM has to detach the device before we delete any implicit diffs.
4618 * If this fails we can roll back without loosing data.
4619 */
4620 if (fHotplug || fSilent)
4621 {
4622 alock.release();
4623 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4624 alock.acquire();
4625 }
4626 if (FAILED(rc)) return rc;
4627
4628 /* If we are here everything went well and we can delete the implicit now. */
4629 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4630
4631 alock.release();
4632
4633 mParent->saveModifiedRegistries();
4634
4635 return rc;
4636}
4637
4638STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4639 LONG aDevice, BOOL aPassthrough)
4640{
4641 CheckComArgStrNotEmptyOrNull(aControllerName);
4642
4643 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4644 aControllerName, aControllerPort, aDevice, aPassthrough));
4645
4646 AutoCaller autoCaller(this);
4647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4648
4649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4650
4651 HRESULT rc = checkStateDependency(MutableStateDep);
4652 if (FAILED(rc)) return rc;
4653
4654 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4655
4656 if (Global::IsOnlineOrTransient(mData->mMachineState))
4657 return setError(VBOX_E_INVALID_VM_STATE,
4658 tr("Invalid machine state: %s"),
4659 Global::stringifyMachineState(mData->mMachineState));
4660
4661 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4662 aControllerName,
4663 aControllerPort,
4664 aDevice);
4665 if (!pAttach)
4666 return setError(VBOX_E_OBJECT_NOT_FOUND,
4667 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4668 aDevice, aControllerPort, aControllerName);
4669
4670
4671 setModified(IsModified_Storage);
4672 mMediaData.backup();
4673
4674 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4675
4676 if (pAttach->getType() != DeviceType_DVD)
4677 return setError(E_INVALIDARG,
4678 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4679 aDevice, aControllerPort, aControllerName);
4680 pAttach->updatePassthrough(!!aPassthrough);
4681
4682 return S_OK;
4683}
4684
4685STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4686 LONG aDevice, BOOL aTemporaryEject)
4687{
4688 CheckComArgStrNotEmptyOrNull(aControllerName);
4689
4690 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4691 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4692
4693 AutoCaller autoCaller(this);
4694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4695
4696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4697
4698 HRESULT rc = checkStateDependency(MutableStateDep);
4699 if (FAILED(rc)) return rc;
4700
4701 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4702 aControllerName,
4703 aControllerPort,
4704 aDevice);
4705 if (!pAttach)
4706 return setError(VBOX_E_OBJECT_NOT_FOUND,
4707 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4708 aDevice, aControllerPort, aControllerName);
4709
4710
4711 setModified(IsModified_Storage);
4712 mMediaData.backup();
4713
4714 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4715
4716 if (pAttach->getType() != DeviceType_DVD)
4717 return setError(E_INVALIDARG,
4718 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4719 aDevice, aControllerPort, aControllerName);
4720 pAttach->updateTempEject(!!aTemporaryEject);
4721
4722 return S_OK;
4723}
4724
4725STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4726 LONG aDevice, BOOL aNonRotational)
4727{
4728 CheckComArgStrNotEmptyOrNull(aControllerName);
4729
4730 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4731 aControllerName, aControllerPort, aDevice, aNonRotational));
4732
4733 AutoCaller autoCaller(this);
4734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4735
4736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4737
4738 HRESULT rc = checkStateDependency(MutableStateDep);
4739 if (FAILED(rc)) return rc;
4740
4741 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4742
4743 if (Global::IsOnlineOrTransient(mData->mMachineState))
4744 return setError(VBOX_E_INVALID_VM_STATE,
4745 tr("Invalid machine state: %s"),
4746 Global::stringifyMachineState(mData->mMachineState));
4747
4748 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4749 aControllerName,
4750 aControllerPort,
4751 aDevice);
4752 if (!pAttach)
4753 return setError(VBOX_E_OBJECT_NOT_FOUND,
4754 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4755 aDevice, aControllerPort, aControllerName);
4756
4757
4758 setModified(IsModified_Storage);
4759 mMediaData.backup();
4760
4761 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4762
4763 if (pAttach->getType() != DeviceType_HardDisk)
4764 return setError(E_INVALIDARG,
4765 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"),
4766 aDevice, aControllerPort, aControllerName);
4767 pAttach->updateNonRotational(!!aNonRotational);
4768
4769 return S_OK;
4770}
4771
4772STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4773 LONG aDevice, BOOL aDiscard)
4774{
4775 CheckComArgStrNotEmptyOrNull(aControllerName);
4776
4777 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4778 aControllerName, aControllerPort, aDevice, aDiscard));
4779
4780 AutoCaller autoCaller(this);
4781 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4782
4783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4784
4785 HRESULT rc = checkStateDependency(MutableStateDep);
4786 if (FAILED(rc)) return rc;
4787
4788 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4789
4790 if (Global::IsOnlineOrTransient(mData->mMachineState))
4791 return setError(VBOX_E_INVALID_VM_STATE,
4792 tr("Invalid machine state: %s"),
4793 Global::stringifyMachineState(mData->mMachineState));
4794
4795 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4796 aControllerName,
4797 aControllerPort,
4798 aDevice);
4799 if (!pAttach)
4800 return setError(VBOX_E_OBJECT_NOT_FOUND,
4801 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4802 aDevice, aControllerPort, aControllerName);
4803
4804
4805 setModified(IsModified_Storage);
4806 mMediaData.backup();
4807
4808 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4809
4810 if (pAttach->getType() != DeviceType_HardDisk)
4811 return setError(E_INVALIDARG,
4812 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"),
4813 aDevice, aControllerPort, aControllerName);
4814 pAttach->updateDiscard(!!aDiscard);
4815
4816 return S_OK;
4817}
4818
4819STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4820 LONG aDevice)
4821{
4822 int rc = S_OK;
4823 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4824 aControllerName, aControllerPort, aDevice));
4825
4826 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4827
4828 return rc;
4829}
4830
4831STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4832 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4833{
4834 CheckComArgStrNotEmptyOrNull(aControllerName);
4835
4836 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4837 aControllerName, aControllerPort, aDevice));
4838
4839 AutoCaller autoCaller(this);
4840 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4841
4842 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4843
4844 HRESULT rc = checkStateDependency(MutableStateDep);
4845 if (FAILED(rc)) return rc;
4846
4847 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4848
4849 if (Global::IsOnlineOrTransient(mData->mMachineState))
4850 return setError(VBOX_E_INVALID_VM_STATE,
4851 tr("Invalid machine state: %s"),
4852 Global::stringifyMachineState(mData->mMachineState));
4853
4854 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4855 aControllerName,
4856 aControllerPort,
4857 aDevice);
4858 if (!pAttach)
4859 return setError(VBOX_E_OBJECT_NOT_FOUND,
4860 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4861 aDevice, aControllerPort, aControllerName);
4862
4863
4864 setModified(IsModified_Storage);
4865 mMediaData.backup();
4866
4867 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4868 if (aBandwidthGroup && group.isNull())
4869 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4870
4871 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4872
4873 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4874 if (strBandwidthGroupOld.isNotEmpty())
4875 {
4876 /* Get the bandwidth group object and release it - this must not fail. */
4877 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4878 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4879 Assert(SUCCEEDED(rc));
4880
4881 pBandwidthGroupOld->release();
4882 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4883 }
4884
4885 if (!group.isNull())
4886 {
4887 group->reference();
4888 pAttach->updateBandwidthGroup(group->getName());
4889 }
4890
4891 return S_OK;
4892}
4893
4894STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4895 LONG aControllerPort,
4896 LONG aDevice,
4897 DeviceType_T aType)
4898{
4899 HRESULT rc = S_OK;
4900
4901 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4902 aControllerName, aControllerPort, aDevice, aType));
4903
4904 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4905
4906 return rc;
4907}
4908
4909
4910
4911STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4912 LONG aControllerPort,
4913 LONG aDevice,
4914 BOOL aForce)
4915{
4916 int rc = S_OK;
4917 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4918 aControllerName, aControllerPort, aForce));
4919
4920 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4921
4922 return rc;
4923}
4924
4925STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4926 LONG aControllerPort,
4927 LONG aDevice,
4928 IMedium *aMedium,
4929 BOOL aForce)
4930{
4931 int rc = S_OK;
4932 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4933 aControllerName, aControllerPort, aDevice, aForce));
4934
4935 CheckComArgStrNotEmptyOrNull(aControllerName);
4936
4937 AutoCaller autoCaller(this);
4938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4939
4940 // request the host lock first, since might be calling Host methods for getting host drives;
4941 // next, protect the media tree all the while we're in here, as well as our member variables
4942 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4943 this->lockHandle(),
4944 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4945
4946 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4947 aControllerName,
4948 aControllerPort,
4949 aDevice);
4950 if (pAttach.isNull())
4951 return setError(VBOX_E_OBJECT_NOT_FOUND,
4952 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4953 aDevice, aControllerPort, aControllerName);
4954
4955 /* Remember previously mounted medium. The medium before taking the
4956 * backup is not necessarily the same thing. */
4957 ComObjPtr<Medium> oldmedium;
4958 oldmedium = pAttach->getMedium();
4959
4960 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4961 if (aMedium && pMedium.isNull())
4962 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4963
4964 AutoCaller mediumCaller(pMedium);
4965 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4966
4967 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4968 if (pMedium)
4969 {
4970 DeviceType_T mediumType = pAttach->getType();
4971 switch (mediumType)
4972 {
4973 case DeviceType_DVD:
4974 case DeviceType_Floppy:
4975 break;
4976
4977 default:
4978 return setError(VBOX_E_INVALID_OBJECT_STATE,
4979 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4980 aControllerPort,
4981 aDevice,
4982 aControllerName);
4983 }
4984 }
4985
4986 setModified(IsModified_Storage);
4987 mMediaData.backup();
4988
4989 {
4990 // The backup operation makes the pAttach reference point to the
4991 // old settings. Re-get the correct reference.
4992 pAttach = findAttachment(mMediaData->mAttachments,
4993 aControllerName,
4994 aControllerPort,
4995 aDevice);
4996 if (!oldmedium.isNull())
4997 oldmedium->removeBackReference(mData->mUuid);
4998 if (!pMedium.isNull())
4999 {
5000 pMedium->addBackReference(mData->mUuid);
5001
5002 mediumLock.release();
5003 multiLock.release();
5004 addMediumToRegistry(pMedium);
5005 multiLock.acquire();
5006 mediumLock.acquire();
5007 }
5008
5009 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5010 pAttach->updateMedium(pMedium);
5011 }
5012
5013 setModified(IsModified_Storage);
5014
5015 mediumLock.release();
5016 multiLock.release();
5017 rc = onMediumChange(pAttach, aForce);
5018 multiLock.acquire();
5019 mediumLock.acquire();
5020
5021 /* On error roll back this change only. */
5022 if (FAILED(rc))
5023 {
5024 if (!pMedium.isNull())
5025 pMedium->removeBackReference(mData->mUuid);
5026 pAttach = findAttachment(mMediaData->mAttachments,
5027 aControllerName,
5028 aControllerPort,
5029 aDevice);
5030 /* If the attachment is gone in the meantime, bail out. */
5031 if (pAttach.isNull())
5032 return rc;
5033 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5034 if (!oldmedium.isNull())
5035 oldmedium->addBackReference(mData->mUuid);
5036 pAttach->updateMedium(oldmedium);
5037 }
5038
5039 mediumLock.release();
5040 multiLock.release();
5041
5042 mParent->saveModifiedRegistries();
5043
5044 return rc;
5045}
5046
5047STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5048 LONG aControllerPort,
5049 LONG aDevice,
5050 IMedium **aMedium)
5051{
5052 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5053 aControllerName, aControllerPort, aDevice));
5054
5055 CheckComArgStrNotEmptyOrNull(aControllerName);
5056 CheckComArgOutPointerValid(aMedium);
5057
5058 AutoCaller autoCaller(this);
5059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5060
5061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5062
5063 *aMedium = NULL;
5064
5065 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5066 aControllerName,
5067 aControllerPort,
5068 aDevice);
5069 if (pAttach.isNull())
5070 return setError(VBOX_E_OBJECT_NOT_FOUND,
5071 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5072 aDevice, aControllerPort, aControllerName);
5073
5074 pAttach->getMedium().queryInterfaceTo(aMedium);
5075
5076 return S_OK;
5077}
5078
5079STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5080{
5081 CheckComArgOutPointerValid(port);
5082 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5083
5084 AutoCaller autoCaller(this);
5085 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5086
5087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5088
5089 mSerialPorts[slot].queryInterfaceTo(port);
5090
5091 return S_OK;
5092}
5093
5094STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5095{
5096 CheckComArgOutPointerValid(port);
5097 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5098
5099 AutoCaller autoCaller(this);
5100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5101
5102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5103
5104 mParallelPorts[slot].queryInterfaceTo(port);
5105
5106 return S_OK;
5107}
5108
5109STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5110{
5111 CheckComArgOutPointerValid(adapter);
5112 /* Do not assert if slot is out of range, just return the advertised
5113 status. testdriver/vbox.py triggers this in logVmInfo. */
5114 if (slot >= mNetworkAdapters.size())
5115 return setError(E_INVALIDARG,
5116 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5117 slot, mNetworkAdapters.size());
5118
5119 AutoCaller autoCaller(this);
5120 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5121
5122 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5123
5124 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5125
5126 return S_OK;
5127}
5128
5129STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5130{
5131 CheckComArgOutSafeArrayPointerValid(aKeys);
5132
5133 AutoCaller autoCaller(this);
5134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5135
5136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5139 int i = 0;
5140 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5141 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5142 ++it, ++i)
5143 {
5144 const Utf8Str &strKey = it->first;
5145 strKey.cloneTo(&saKeys[i]);
5146 }
5147 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5148
5149 return S_OK;
5150 }
5151
5152 /**
5153 * @note Locks this object for reading.
5154 */
5155STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5156 BSTR *aValue)
5157{
5158 CheckComArgStrNotEmptyOrNull(aKey);
5159 CheckComArgOutPointerValid(aValue);
5160
5161 AutoCaller autoCaller(this);
5162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5163
5164 /* start with nothing found */
5165 Bstr bstrResult("");
5166
5167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5170 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5171 // found:
5172 bstrResult = it->second; // source is a Utf8Str
5173
5174 /* return the result to caller (may be empty) */
5175 bstrResult.cloneTo(aValue);
5176
5177 return S_OK;
5178}
5179
5180 /**
5181 * @note Locks mParent for writing + this object for writing.
5182 */
5183STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5184{
5185 CheckComArgStrNotEmptyOrNull(aKey);
5186
5187 AutoCaller autoCaller(this);
5188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5189
5190 Utf8Str strKey(aKey);
5191 Utf8Str strValue(aValue);
5192 Utf8Str strOldValue; // empty
5193
5194 // locking note: we only hold the read lock briefly to look up the old value,
5195 // then release it and call the onExtraCanChange callbacks. There is a small
5196 // chance of a race insofar as the callback might be called twice if two callers
5197 // change the same key at the same time, but that's a much better solution
5198 // than the deadlock we had here before. The actual changing of the extradata
5199 // is then performed under the write lock and race-free.
5200
5201 // look up the old value first; if nothing has changed then we need not do anything
5202 {
5203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5204 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5205 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5206 strOldValue = it->second;
5207 }
5208
5209 bool fChanged;
5210 if ((fChanged = (strOldValue != strValue)))
5211 {
5212 // ask for permission from all listeners outside the locks;
5213 // onExtraDataCanChange() only briefly requests the VirtualBox
5214 // lock to copy the list of callbacks to invoke
5215 Bstr error;
5216 Bstr bstrValue(aValue);
5217
5218 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5219 {
5220 const char *sep = error.isEmpty() ? "" : ": ";
5221 CBSTR err = error.raw();
5222 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5223 sep, err));
5224 return setError(E_ACCESSDENIED,
5225 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5226 aKey,
5227 bstrValue.raw(),
5228 sep,
5229 err);
5230 }
5231
5232 // data is changing and change not vetoed: then write it out under the lock
5233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5234
5235 if (isSnapshotMachine())
5236 {
5237 HRESULT rc = checkStateDependency(MutableStateDep);
5238 if (FAILED(rc)) return rc;
5239 }
5240
5241 if (strValue.isEmpty())
5242 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5243 else
5244 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5245 // creates a new key if needed
5246
5247 bool fNeedsGlobalSaveSettings = false;
5248 saveSettings(&fNeedsGlobalSaveSettings);
5249
5250 if (fNeedsGlobalSaveSettings)
5251 {
5252 // save the global settings; for that we should hold only the VirtualBox lock
5253 alock.release();
5254 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5255 mParent->saveSettings();
5256 }
5257 }
5258
5259 // fire notification outside the lock
5260 if (fChanged)
5261 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5262
5263 return S_OK;
5264}
5265
5266STDMETHODIMP Machine::SaveSettings()
5267{
5268 AutoCaller autoCaller(this);
5269 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5270
5271 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5272
5273 /* when there was auto-conversion, we want to save the file even if
5274 * the VM is saved */
5275 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5276 if (FAILED(rc)) return rc;
5277
5278 /* the settings file path may never be null */
5279 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5280
5281 /* save all VM data excluding snapshots */
5282 bool fNeedsGlobalSaveSettings = false;
5283 rc = saveSettings(&fNeedsGlobalSaveSettings);
5284 mlock.release();
5285
5286 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5287 {
5288 // save the global settings; for that we should hold only the VirtualBox lock
5289 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5290 rc = mParent->saveSettings();
5291 }
5292
5293 return rc;
5294}
5295
5296STDMETHODIMP Machine::DiscardSettings()
5297{
5298 AutoCaller autoCaller(this);
5299 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5300
5301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5302
5303 HRESULT rc = checkStateDependency(MutableStateDep);
5304 if (FAILED(rc)) return rc;
5305
5306 /*
5307 * during this rollback, the session will be notified if data has
5308 * been actually changed
5309 */
5310 rollback(true /* aNotify */);
5311
5312 return S_OK;
5313}
5314
5315/** @note Locks objects! */
5316STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5317 ComSafeArrayOut(IMedium*, aMedia))
5318{
5319 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5320 AutoLimitedCaller autoCaller(this);
5321 AssertComRCReturnRC(autoCaller.rc());
5322
5323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5324
5325 Guid id(getId());
5326
5327 if (mData->mSession.mState != SessionState_Unlocked)
5328 return setError(VBOX_E_INVALID_OBJECT_STATE,
5329 tr("Cannot unregister the machine '%s' while it is locked"),
5330 mUserData->s.strName.c_str());
5331
5332 // wait for state dependents to drop to zero
5333 ensureNoStateDependencies();
5334
5335 if (!mData->mAccessible)
5336 {
5337 // inaccessible maschines can only be unregistered; uninitialize ourselves
5338 // here because currently there may be no unregistered that are inaccessible
5339 // (this state combination is not supported). Note releasing the caller and
5340 // leaving the lock before calling uninit()
5341 alock.release();
5342 autoCaller.release();
5343
5344 uninit();
5345
5346 mParent->unregisterMachine(this, id);
5347 // calls VirtualBox::saveSettings()
5348
5349 return S_OK;
5350 }
5351
5352 HRESULT rc = S_OK;
5353
5354 // discard saved state
5355 if (mData->mMachineState == MachineState_Saved)
5356 {
5357 // add the saved state file to the list of files the caller should delete
5358 Assert(!mSSData->strStateFilePath.isEmpty());
5359 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5360
5361 mSSData->strStateFilePath.setNull();
5362
5363 // unconditionally set the machine state to powered off, we now
5364 // know no session has locked the machine
5365 mData->mMachineState = MachineState_PoweredOff;
5366 }
5367
5368 size_t cSnapshots = 0;
5369 if (mData->mFirstSnapshot)
5370 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5371 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5372 // fail now before we start detaching media
5373 return setError(VBOX_E_INVALID_OBJECT_STATE,
5374 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5375 mUserData->s.strName.c_str(), cSnapshots);
5376
5377 // This list collects the medium objects from all medium attachments
5378 // which we will detach from the machine and its snapshots, in a specific
5379 // order which allows for closing all media without getting "media in use"
5380 // errors, simply by going through the list from the front to the back:
5381 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5382 // and must be closed before the parent media from the snapshots, or closing the parents
5383 // will fail because they still have children);
5384 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5385 // the root ("first") snapshot of the machine.
5386 MediaList llMedia;
5387
5388 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5389 && mMediaData->mAttachments.size()
5390 )
5391 {
5392 // we have media attachments: detach them all and add the Medium objects to our list
5393 if (cleanupMode != CleanupMode_UnregisterOnly)
5394 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5395 else
5396 return setError(VBOX_E_INVALID_OBJECT_STATE,
5397 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5398 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5399 }
5400
5401 if (cSnapshots)
5402 {
5403 // autoCleanup must be true here, or we would have failed above
5404
5405 // add the media from the medium attachments of the snapshots to llMedia
5406 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5407 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5408 // into the children first
5409
5410 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5411 MachineState_T oldState = mData->mMachineState;
5412 mData->mMachineState = MachineState_DeletingSnapshot;
5413
5414 // make a copy of the first snapshot so the refcount does not drop to 0
5415 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5416 // because of the AutoCaller voodoo)
5417 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5418
5419 // GO!
5420 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5421
5422 mData->mMachineState = oldState;
5423 }
5424
5425 if (FAILED(rc))
5426 {
5427 rollbackMedia();
5428 return rc;
5429 }
5430
5431 // commit all the media changes made above
5432 commitMedia();
5433
5434 mData->mRegistered = false;
5435
5436 // machine lock no longer needed
5437 alock.release();
5438
5439 // return media to caller
5440 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5441 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5442
5443 mParent->unregisterMachine(this, id);
5444 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5445
5446 return S_OK;
5447}
5448
5449struct Machine::DeleteTask
5450{
5451 ComObjPtr<Machine> pMachine;
5452 RTCList<ComPtr<IMedium> > llMediums;
5453 StringsList llFilesToDelete;
5454 ComObjPtr<Progress> pProgress;
5455};
5456
5457STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5458{
5459 LogFlowFuncEnter();
5460
5461 AutoCaller autoCaller(this);
5462 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5463
5464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5465
5466 HRESULT rc = checkStateDependency(MutableStateDep);
5467 if (FAILED(rc)) return rc;
5468
5469 if (mData->mRegistered)
5470 return setError(VBOX_E_INVALID_VM_STATE,
5471 tr("Cannot delete settings of a registered machine"));
5472
5473 DeleteTask *pTask = new DeleteTask;
5474 pTask->pMachine = this;
5475 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5476
5477 // collect files to delete
5478 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5479
5480 for (size_t i = 0; i < sfaMedia.size(); ++i)
5481 {
5482 IMedium *pIMedium(sfaMedia[i]);
5483 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5484 if (pMedium.isNull())
5485 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5486 SafeArray<BSTR> ids;
5487 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5488 if (FAILED(rc)) return rc;
5489 /* At this point the medium should not have any back references
5490 * anymore. If it has it is attached to another VM and *must* not
5491 * deleted. */
5492 if (ids.size() < 1)
5493 pTask->llMediums.append(pMedium);
5494 }
5495 if (mData->pMachineConfigFile->fileExists())
5496 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5497
5498 pTask->pProgress.createObject();
5499 pTask->pProgress->init(getVirtualBox(),
5500 static_cast<IMachine*>(this) /* aInitiator */,
5501 Bstr(tr("Deleting files")).raw(),
5502 true /* fCancellable */,
5503 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5504 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5505
5506 int vrc = RTThreadCreate(NULL,
5507 Machine::deleteThread,
5508 (void*)pTask,
5509 0,
5510 RTTHREADTYPE_MAIN_WORKER,
5511 0,
5512 "MachineDelete");
5513
5514 pTask->pProgress.queryInterfaceTo(aProgress);
5515
5516 if (RT_FAILURE(vrc))
5517 {
5518 delete pTask;
5519 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5520 }
5521
5522 LogFlowFuncLeave();
5523
5524 return S_OK;
5525}
5526
5527/**
5528 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5529 * calls Machine::deleteTaskWorker() on the actual machine object.
5530 * @param Thread
5531 * @param pvUser
5532 * @return
5533 */
5534/*static*/
5535DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5536{
5537 LogFlowFuncEnter();
5538
5539 DeleteTask *pTask = (DeleteTask*)pvUser;
5540 Assert(pTask);
5541 Assert(pTask->pMachine);
5542 Assert(pTask->pProgress);
5543
5544 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5545 pTask->pProgress->notifyComplete(rc);
5546
5547 delete pTask;
5548
5549 LogFlowFuncLeave();
5550
5551 NOREF(Thread);
5552
5553 return VINF_SUCCESS;
5554}
5555
5556/**
5557 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5558 * @param task
5559 * @return
5560 */
5561HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5562{
5563 AutoCaller autoCaller(this);
5564 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5565
5566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5567
5568 HRESULT rc = S_OK;
5569
5570 try
5571 {
5572 ULONG uLogHistoryCount = 3;
5573 ComPtr<ISystemProperties> systemProperties;
5574 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5575 if (FAILED(rc)) throw rc;
5576
5577 if (!systemProperties.isNull())
5578 {
5579 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5580 if (FAILED(rc)) throw rc;
5581 }
5582
5583 MachineState_T oldState = mData->mMachineState;
5584 setMachineState(MachineState_SettingUp);
5585 alock.release();
5586 for (size_t i = 0; i < task.llMediums.size(); ++i)
5587 {
5588 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5589 {
5590 AutoCaller mac(pMedium);
5591 if (FAILED(mac.rc())) throw mac.rc();
5592 Utf8Str strLocation = pMedium->getLocationFull();
5593 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5594 if (FAILED(rc)) throw rc;
5595 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5596 }
5597 ComPtr<IProgress> pProgress2;
5598 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5599 if (FAILED(rc)) throw rc;
5600 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5601 if (FAILED(rc)) throw rc;
5602 /* Check the result of the asynchrony process. */
5603 LONG iRc;
5604 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5605 if (FAILED(rc)) throw rc;
5606 /* If the thread of the progress object has an error, then
5607 * retrieve the error info from there, or it'll be lost. */
5608 if (FAILED(iRc))
5609 throw setError(ProgressErrorInfo(pProgress2));
5610 }
5611 setMachineState(oldState);
5612 alock.acquire();
5613
5614 // delete the files pushed on the task list by Machine::Delete()
5615 // (this includes saved states of the machine and snapshots and
5616 // medium storage files from the IMedium list passed in, and the
5617 // machine XML file)
5618 StringsList::const_iterator it = task.llFilesToDelete.begin();
5619 while (it != task.llFilesToDelete.end())
5620 {
5621 const Utf8Str &strFile = *it;
5622 LogFunc(("Deleting file %s\n", strFile.c_str()));
5623 int vrc = RTFileDelete(strFile.c_str());
5624 if (RT_FAILURE(vrc))
5625 throw setError(VBOX_E_IPRT_ERROR,
5626 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5627
5628 ++it;
5629 if (it == task.llFilesToDelete.end())
5630 {
5631 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5632 if (FAILED(rc)) throw rc;
5633 break;
5634 }
5635
5636 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5637 if (FAILED(rc)) throw rc;
5638 }
5639
5640 /* delete the settings only when the file actually exists */
5641 if (mData->pMachineConfigFile->fileExists())
5642 {
5643 /* Delete any backup or uncommitted XML files. Ignore failures.
5644 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5645 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5646 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5647 RTFileDelete(otherXml.c_str());
5648 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5649 RTFileDelete(otherXml.c_str());
5650
5651 /* delete the Logs folder, nothing important should be left
5652 * there (we don't check for errors because the user might have
5653 * some private files there that we don't want to delete) */
5654 Utf8Str logFolder;
5655 getLogFolder(logFolder);
5656 Assert(logFolder.length());
5657 if (RTDirExists(logFolder.c_str()))
5658 {
5659 /* Delete all VBox.log[.N] files from the Logs folder
5660 * (this must be in sync with the rotation logic in
5661 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5662 * files that may have been created by the GUI. */
5663 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5664 logFolder.c_str(), RTPATH_DELIMITER);
5665 RTFileDelete(log.c_str());
5666 log = Utf8StrFmt("%s%cVBox.png",
5667 logFolder.c_str(), RTPATH_DELIMITER);
5668 RTFileDelete(log.c_str());
5669 for (int i = uLogHistoryCount; i > 0; i--)
5670 {
5671 log = Utf8StrFmt("%s%cVBox.log.%d",
5672 logFolder.c_str(), RTPATH_DELIMITER, i);
5673 RTFileDelete(log.c_str());
5674 log = Utf8StrFmt("%s%cVBox.png.%d",
5675 logFolder.c_str(), RTPATH_DELIMITER, i);
5676 RTFileDelete(log.c_str());
5677 }
5678
5679 RTDirRemove(logFolder.c_str());
5680 }
5681
5682 /* delete the Snapshots folder, nothing important should be left
5683 * there (we don't check for errors because the user might have
5684 * some private files there that we don't want to delete) */
5685 Utf8Str strFullSnapshotFolder;
5686 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5687 Assert(!strFullSnapshotFolder.isEmpty());
5688 if (RTDirExists(strFullSnapshotFolder.c_str()))
5689 RTDirRemove(strFullSnapshotFolder.c_str());
5690
5691 // delete the directory that contains the settings file, but only
5692 // if it matches the VM name
5693 Utf8Str settingsDir;
5694 if (isInOwnDir(&settingsDir))
5695 RTDirRemove(settingsDir.c_str());
5696 }
5697
5698 alock.release();
5699
5700 mParent->saveModifiedRegistries();
5701 }
5702 catch (HRESULT aRC) { rc = aRC; }
5703
5704 return rc;
5705}
5706
5707STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5708{
5709 CheckComArgOutPointerValid(aSnapshot);
5710
5711 AutoCaller autoCaller(this);
5712 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5713
5714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5715
5716 ComObjPtr<Snapshot> pSnapshot;
5717 HRESULT rc;
5718
5719 if (!aNameOrId || !*aNameOrId)
5720 // null case (caller wants root snapshot): findSnapshotById() handles this
5721 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5722 else
5723 {
5724 Guid uuid(aNameOrId);
5725 if (uuid.isValid())
5726 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5727 else
5728 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5729 }
5730 pSnapshot.queryInterfaceTo(aSnapshot);
5731
5732 return rc;
5733}
5734
5735STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5736{
5737 CheckComArgStrNotEmptyOrNull(aName);
5738 CheckComArgStrNotEmptyOrNull(aHostPath);
5739
5740 AutoCaller autoCaller(this);
5741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5742
5743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5744
5745 HRESULT rc = checkStateDependency(MutableStateDep);
5746 if (FAILED(rc)) return rc;
5747
5748 Utf8Str strName(aName);
5749
5750 ComObjPtr<SharedFolder> sharedFolder;
5751 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5752 if (SUCCEEDED(rc))
5753 return setError(VBOX_E_OBJECT_IN_USE,
5754 tr("Shared folder named '%s' already exists"),
5755 strName.c_str());
5756
5757 sharedFolder.createObject();
5758 rc = sharedFolder->init(getMachine(),
5759 strName,
5760 aHostPath,
5761 !!aWritable,
5762 !!aAutoMount,
5763 true /* fFailOnError */);
5764 if (FAILED(rc)) return rc;
5765
5766 setModified(IsModified_SharedFolders);
5767 mHWData.backup();
5768 mHWData->mSharedFolders.push_back(sharedFolder);
5769
5770 /* inform the direct session if any */
5771 alock.release();
5772 onSharedFolderChange();
5773
5774 return S_OK;
5775}
5776
5777STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5778{
5779 CheckComArgStrNotEmptyOrNull(aName);
5780
5781 AutoCaller autoCaller(this);
5782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5783
5784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5785
5786 HRESULT rc = checkStateDependency(MutableStateDep);
5787 if (FAILED(rc)) return rc;
5788
5789 ComObjPtr<SharedFolder> sharedFolder;
5790 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5791 if (FAILED(rc)) return rc;
5792
5793 setModified(IsModified_SharedFolders);
5794 mHWData.backup();
5795 mHWData->mSharedFolders.remove(sharedFolder);
5796
5797 /* inform the direct session if any */
5798 alock.release();
5799 onSharedFolderChange();
5800
5801 return S_OK;
5802}
5803
5804STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5805{
5806 CheckComArgOutPointerValid(aCanShow);
5807
5808 /* start with No */
5809 *aCanShow = FALSE;
5810
5811 AutoCaller autoCaller(this);
5812 AssertComRCReturnRC(autoCaller.rc());
5813
5814 ComPtr<IInternalSessionControl> directControl;
5815 {
5816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5817
5818 if (mData->mSession.mState != SessionState_Locked)
5819 return setError(VBOX_E_INVALID_VM_STATE,
5820 tr("Machine is not locked for session (session state: %s)"),
5821 Global::stringifySessionState(mData->mSession.mState));
5822
5823 directControl = mData->mSession.mDirectControl;
5824 }
5825
5826 /* ignore calls made after #OnSessionEnd() is called */
5827 if (!directControl)
5828 return S_OK;
5829
5830 LONG64 dummy;
5831 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5832}
5833
5834STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5835{
5836 CheckComArgOutPointerValid(aWinId);
5837
5838 AutoCaller autoCaller(this);
5839 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5840
5841 ComPtr<IInternalSessionControl> directControl;
5842 {
5843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5844
5845 if (mData->mSession.mState != SessionState_Locked)
5846 return setError(E_FAIL,
5847 tr("Machine is not locked for session (session state: %s)"),
5848 Global::stringifySessionState(mData->mSession.mState));
5849
5850 directControl = mData->mSession.mDirectControl;
5851 }
5852
5853 /* ignore calls made after #OnSessionEnd() is called */
5854 if (!directControl)
5855 return S_OK;
5856
5857 BOOL dummy;
5858 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5859}
5860
5861#ifdef VBOX_WITH_GUEST_PROPS
5862/**
5863 * Look up a guest property in VBoxSVC's internal structures.
5864 */
5865HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5866 BSTR *aValue,
5867 LONG64 *aTimestamp,
5868 BSTR *aFlags) const
5869{
5870 using namespace guestProp;
5871
5872 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5873 Utf8Str strName(aName);
5874 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5875
5876 if (it != mHWData->mGuestProperties.end())
5877 {
5878 char szFlags[MAX_FLAGS_LEN + 1];
5879 it->second.strValue.cloneTo(aValue);
5880 *aTimestamp = it->second.mTimestamp;
5881 writeFlags(it->second.mFlags, szFlags);
5882 Bstr(szFlags).cloneTo(aFlags);
5883 }
5884
5885 return S_OK;
5886}
5887
5888/**
5889 * Query the VM that a guest property belongs to for the property.
5890 * @returns E_ACCESSDENIED if the VM process is not available or not
5891 * currently handling queries and the lookup should then be done in
5892 * VBoxSVC.
5893 */
5894HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5895 BSTR *aValue,
5896 LONG64 *aTimestamp,
5897 BSTR *aFlags) const
5898{
5899 HRESULT rc;
5900 ComPtr<IInternalSessionControl> directControl;
5901 directControl = mData->mSession.mDirectControl;
5902
5903 /* fail if we were called after #OnSessionEnd() is called. This is a
5904 * silly race condition. */
5905
5906 if (!directControl)
5907 rc = E_ACCESSDENIED;
5908 else
5909 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5910 false /* isSetter */,
5911 aValue, aTimestamp, aFlags);
5912 return rc;
5913}
5914#endif // VBOX_WITH_GUEST_PROPS
5915
5916STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5917 BSTR *aValue,
5918 LONG64 *aTimestamp,
5919 BSTR *aFlags)
5920{
5921#ifndef VBOX_WITH_GUEST_PROPS
5922 ReturnComNotImplemented();
5923#else // VBOX_WITH_GUEST_PROPS
5924 CheckComArgStrNotEmptyOrNull(aName);
5925 CheckComArgOutPointerValid(aValue);
5926 CheckComArgOutPointerValid(aTimestamp);
5927 CheckComArgOutPointerValid(aFlags);
5928
5929 AutoCaller autoCaller(this);
5930 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5931
5932 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5933 if (rc == E_ACCESSDENIED)
5934 /* The VM is not running or the service is not (yet) accessible */
5935 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5936 return rc;
5937#endif // VBOX_WITH_GUEST_PROPS
5938}
5939
5940STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5941{
5942 LONG64 dummyTimestamp;
5943 Bstr dummyFlags;
5944 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5945}
5946
5947STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5948{
5949 Bstr dummyValue;
5950 Bstr dummyFlags;
5951 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5952}
5953
5954#ifdef VBOX_WITH_GUEST_PROPS
5955/**
5956 * Set a guest property in VBoxSVC's internal structures.
5957 */
5958HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5959 IN_BSTR aFlags)
5960{
5961 using namespace guestProp;
5962
5963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5964 HRESULT rc = S_OK;
5965
5966 rc = checkStateDependency(MutableStateDep);
5967 if (FAILED(rc)) return rc;
5968
5969 try
5970 {
5971 Utf8Str utf8Name(aName);
5972 Utf8Str utf8Flags(aFlags);
5973 uint32_t fFlags = NILFLAG;
5974 if ( aFlags != NULL
5975 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
5976 return setError(E_INVALIDARG,
5977 tr("Invalid guest property flag values: '%ls'"),
5978 aFlags);
5979
5980 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
5981 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
5982 if (it == mHWData->mGuestProperties.end())
5983 {
5984 if (!fDelete)
5985 {
5986 setModified(IsModified_MachineData);
5987 mHWData.backupEx();
5988
5989 RTTIMESPEC time;
5990 HWData::GuestProperty prop;
5991 prop.strValue = aValue;
5992 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5993 prop.mFlags = fFlags;
5994 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5995 }
5996 }
5997 else
5998 {
5999 if (it->second.mFlags & (RDONLYHOST))
6000 {
6001 rc = setError(E_ACCESSDENIED,
6002 tr("The property '%ls' cannot be changed by the host"),
6003 aName);
6004 }
6005 else
6006 {
6007 setModified(IsModified_MachineData);
6008 mHWData.backupEx();
6009
6010 /* The backupEx() operation invalidates our iterator,
6011 * so get a new one. */
6012 it = mHWData->mGuestProperties.find(utf8Name);
6013 Assert(it != mHWData->mGuestProperties.end());
6014
6015 if (!fDelete)
6016 {
6017 RTTIMESPEC time;
6018 it->second.strValue = aValue;
6019 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6020 it->second.mFlags = fFlags;
6021 }
6022 else
6023 mHWData->mGuestProperties.erase(it);
6024 }
6025 }
6026
6027 if ( SUCCEEDED(rc)
6028 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6029 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6030 RTSTR_MAX,
6031 utf8Name.c_str(),
6032 RTSTR_MAX,
6033 NULL)
6034 )
6035 )
6036 {
6037 alock.release();
6038
6039 mParent->onGuestPropertyChange(mData->mUuid, aName,
6040 aValue ? aValue : Bstr("").raw(),
6041 aFlags ? aFlags : Bstr("").raw());
6042 }
6043 }
6044 catch (std::bad_alloc &)
6045 {
6046 rc = E_OUTOFMEMORY;
6047 }
6048
6049 return rc;
6050}
6051
6052/**
6053 * Set a property on the VM that that property belongs to.
6054 * @returns E_ACCESSDENIED if the VM process is not available or not
6055 * currently handling queries and the setting should then be done in
6056 * VBoxSVC.
6057 */
6058HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6059 IN_BSTR aFlags)
6060{
6061 HRESULT rc;
6062
6063 try
6064 {
6065 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6066
6067 BSTR dummy = NULL; /* will not be changed (setter) */
6068 LONG64 dummy64;
6069 if (!directControl)
6070 rc = E_ACCESSDENIED;
6071 else
6072 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6073 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6074 true /* isSetter */,
6075 &dummy, &dummy64, &dummy);
6076 }
6077 catch (std::bad_alloc &)
6078 {
6079 rc = E_OUTOFMEMORY;
6080 }
6081
6082 return rc;
6083}
6084#endif // VBOX_WITH_GUEST_PROPS
6085
6086STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6087 IN_BSTR aFlags)
6088{
6089#ifndef VBOX_WITH_GUEST_PROPS
6090 ReturnComNotImplemented();
6091#else // VBOX_WITH_GUEST_PROPS
6092 CheckComArgStrNotEmptyOrNull(aName);
6093 CheckComArgMaybeNull(aFlags);
6094 CheckComArgMaybeNull(aValue);
6095
6096 AutoCaller autoCaller(this);
6097 if (FAILED(autoCaller.rc()))
6098 return autoCaller.rc();
6099
6100 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6101 if (rc == E_ACCESSDENIED)
6102 /* The VM is not running or the service is not (yet) accessible */
6103 rc = setGuestPropertyToService(aName, aValue, aFlags);
6104 return rc;
6105#endif // VBOX_WITH_GUEST_PROPS
6106}
6107
6108STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6109{
6110 return SetGuestProperty(aName, aValue, NULL);
6111}
6112
6113STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6114{
6115 return SetGuestProperty(aName, NULL, NULL);
6116}
6117
6118#ifdef VBOX_WITH_GUEST_PROPS
6119/**
6120 * Enumerate the guest properties in VBoxSVC's internal structures.
6121 */
6122HRESULT Machine::enumerateGuestPropertiesInService
6123 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6124 ComSafeArrayOut(BSTR, aValues),
6125 ComSafeArrayOut(LONG64, aTimestamps),
6126 ComSafeArrayOut(BSTR, aFlags))
6127{
6128 using namespace guestProp;
6129
6130 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6131 Utf8Str strPatterns(aPatterns);
6132
6133 HWData::GuestPropertyMap propMap;
6134
6135 /*
6136 * Look for matching patterns and build up a list.
6137 */
6138 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6139 while (it != mHWData->mGuestProperties.end())
6140 {
6141 if ( strPatterns.isEmpty()
6142 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6143 RTSTR_MAX,
6144 it->first.c_str(),
6145 RTSTR_MAX,
6146 NULL)
6147 )
6148 {
6149 propMap.insert(*it);
6150 }
6151
6152 it++;
6153 }
6154
6155 alock.release();
6156
6157 /*
6158 * And build up the arrays for returning the property information.
6159 */
6160 size_t cEntries = propMap.size();
6161 SafeArray<BSTR> names(cEntries);
6162 SafeArray<BSTR> values(cEntries);
6163 SafeArray<LONG64> timestamps(cEntries);
6164 SafeArray<BSTR> flags(cEntries);
6165 size_t iProp = 0;
6166
6167 it = propMap.begin();
6168 while (it != propMap.end())
6169 {
6170 char szFlags[MAX_FLAGS_LEN + 1];
6171 it->first.cloneTo(&names[iProp]);
6172 it->second.strValue.cloneTo(&values[iProp]);
6173 timestamps[iProp] = it->second.mTimestamp;
6174 writeFlags(it->second.mFlags, szFlags);
6175 Bstr(szFlags).cloneTo(&flags[iProp++]);
6176 it++;
6177 }
6178 names.detachTo(ComSafeArrayOutArg(aNames));
6179 values.detachTo(ComSafeArrayOutArg(aValues));
6180 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6181 flags.detachTo(ComSafeArrayOutArg(aFlags));
6182 return S_OK;
6183}
6184
6185/**
6186 * Enumerate the properties managed by a VM.
6187 * @returns E_ACCESSDENIED if the VM process is not available or not
6188 * currently handling queries and the setting should then be done in
6189 * VBoxSVC.
6190 */
6191HRESULT Machine::enumerateGuestPropertiesOnVM
6192 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6193 ComSafeArrayOut(BSTR, aValues),
6194 ComSafeArrayOut(LONG64, aTimestamps),
6195 ComSafeArrayOut(BSTR, aFlags))
6196{
6197 HRESULT rc;
6198 ComPtr<IInternalSessionControl> directControl;
6199 directControl = mData->mSession.mDirectControl;
6200
6201 if (!directControl)
6202 rc = E_ACCESSDENIED;
6203 else
6204 rc = directControl->EnumerateGuestProperties
6205 (aPatterns, ComSafeArrayOutArg(aNames),
6206 ComSafeArrayOutArg(aValues),
6207 ComSafeArrayOutArg(aTimestamps),
6208 ComSafeArrayOutArg(aFlags));
6209 return rc;
6210}
6211#endif // VBOX_WITH_GUEST_PROPS
6212
6213STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6214 ComSafeArrayOut(BSTR, aNames),
6215 ComSafeArrayOut(BSTR, aValues),
6216 ComSafeArrayOut(LONG64, aTimestamps),
6217 ComSafeArrayOut(BSTR, aFlags))
6218{
6219#ifndef VBOX_WITH_GUEST_PROPS
6220 ReturnComNotImplemented();
6221#else // VBOX_WITH_GUEST_PROPS
6222 CheckComArgMaybeNull(aPatterns);
6223 CheckComArgOutSafeArrayPointerValid(aNames);
6224 CheckComArgOutSafeArrayPointerValid(aValues);
6225 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6226 CheckComArgOutSafeArrayPointerValid(aFlags);
6227
6228 AutoCaller autoCaller(this);
6229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6230
6231 HRESULT rc = enumerateGuestPropertiesOnVM
6232 (aPatterns, ComSafeArrayOutArg(aNames),
6233 ComSafeArrayOutArg(aValues),
6234 ComSafeArrayOutArg(aTimestamps),
6235 ComSafeArrayOutArg(aFlags));
6236 if (rc == E_ACCESSDENIED)
6237 /* The VM is not running or the service is not (yet) accessible */
6238 rc = enumerateGuestPropertiesInService
6239 (aPatterns, ComSafeArrayOutArg(aNames),
6240 ComSafeArrayOutArg(aValues),
6241 ComSafeArrayOutArg(aTimestamps),
6242 ComSafeArrayOutArg(aFlags));
6243 return rc;
6244#endif // VBOX_WITH_GUEST_PROPS
6245}
6246
6247STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6248 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6249{
6250 MediaData::AttachmentList atts;
6251
6252 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6253 if (FAILED(rc)) return rc;
6254
6255 SafeIfaceArray<IMediumAttachment> attachments(atts);
6256 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6257
6258 return S_OK;
6259}
6260
6261STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6262 LONG aControllerPort,
6263 LONG aDevice,
6264 IMediumAttachment **aAttachment)
6265{
6266 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6267 aControllerName, aControllerPort, aDevice));
6268
6269 CheckComArgStrNotEmptyOrNull(aControllerName);
6270 CheckComArgOutPointerValid(aAttachment);
6271
6272 AutoCaller autoCaller(this);
6273 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6274
6275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6276
6277 *aAttachment = NULL;
6278
6279 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6280 aControllerName,
6281 aControllerPort,
6282 aDevice);
6283 if (pAttach.isNull())
6284 return setError(VBOX_E_OBJECT_NOT_FOUND,
6285 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6286 aDevice, aControllerPort, aControllerName);
6287
6288 pAttach.queryInterfaceTo(aAttachment);
6289
6290 return S_OK;
6291}
6292
6293STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6294 StorageBus_T aConnectionType,
6295 IStorageController **controller)
6296{
6297 CheckComArgStrNotEmptyOrNull(aName);
6298
6299 if ( (aConnectionType <= StorageBus_Null)
6300 || (aConnectionType > StorageBus_SAS))
6301 return setError(E_INVALIDARG,
6302 tr("Invalid connection type: %d"),
6303 aConnectionType);
6304
6305 AutoCaller autoCaller(this);
6306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6307
6308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6309
6310 HRESULT rc = checkStateDependency(MutableStateDep);
6311 if (FAILED(rc)) return rc;
6312
6313 /* try to find one with the name first. */
6314 ComObjPtr<StorageController> ctrl;
6315
6316 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6317 if (SUCCEEDED(rc))
6318 return setError(VBOX_E_OBJECT_IN_USE,
6319 tr("Storage controller named '%ls' already exists"),
6320 aName);
6321
6322 ctrl.createObject();
6323
6324 /* get a new instance number for the storage controller */
6325 ULONG ulInstance = 0;
6326 bool fBootable = true;
6327 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6328 it != mStorageControllers->end();
6329 ++it)
6330 {
6331 if ((*it)->getStorageBus() == aConnectionType)
6332 {
6333 ULONG ulCurInst = (*it)->getInstance();
6334
6335 if (ulCurInst >= ulInstance)
6336 ulInstance = ulCurInst + 1;
6337
6338 /* Only one controller of each type can be marked as bootable. */
6339 if ((*it)->getBootable())
6340 fBootable = false;
6341 }
6342 }
6343
6344 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6345 if (FAILED(rc)) return rc;
6346
6347 setModified(IsModified_Storage);
6348 mStorageControllers.backup();
6349 mStorageControllers->push_back(ctrl);
6350
6351 ctrl.queryInterfaceTo(controller);
6352
6353 /* inform the direct session if any */
6354 alock.release();
6355 onStorageControllerChange();
6356
6357 return S_OK;
6358}
6359
6360STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6361 IStorageController **aStorageController)
6362{
6363 CheckComArgStrNotEmptyOrNull(aName);
6364
6365 AutoCaller autoCaller(this);
6366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6367
6368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6369
6370 ComObjPtr<StorageController> ctrl;
6371
6372 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6373 if (SUCCEEDED(rc))
6374 ctrl.queryInterfaceTo(aStorageController);
6375
6376 return rc;
6377}
6378
6379STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6380 IStorageController **aStorageController)
6381{
6382 AutoCaller autoCaller(this);
6383 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6384
6385 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6386
6387 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6388 it != mStorageControllers->end();
6389 ++it)
6390 {
6391 if ((*it)->getInstance() == aInstance)
6392 {
6393 (*it).queryInterfaceTo(aStorageController);
6394 return S_OK;
6395 }
6396 }
6397
6398 return setError(VBOX_E_OBJECT_NOT_FOUND,
6399 tr("Could not find a storage controller with instance number '%lu'"),
6400 aInstance);
6401}
6402
6403STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6404{
6405 AutoCaller autoCaller(this);
6406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6407
6408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6409
6410 HRESULT rc = checkStateDependency(MutableStateDep);
6411 if (FAILED(rc)) return rc;
6412
6413 ComObjPtr<StorageController> ctrl;
6414
6415 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6416 if (SUCCEEDED(rc))
6417 {
6418 /* Ensure that only one controller of each type is marked as bootable. */
6419 if (fBootable == TRUE)
6420 {
6421 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6422 it != mStorageControllers->end();
6423 ++it)
6424 {
6425 ComObjPtr<StorageController> aCtrl = (*it);
6426
6427 if ( (aCtrl->getName() != Utf8Str(aName))
6428 && aCtrl->getBootable() == TRUE
6429 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6430 && aCtrl->getControllerType() == ctrl->getControllerType())
6431 {
6432 aCtrl->setBootable(FALSE);
6433 break;
6434 }
6435 }
6436 }
6437
6438 if (SUCCEEDED(rc))
6439 {
6440 ctrl->setBootable(fBootable);
6441 setModified(IsModified_Storage);
6442 }
6443 }
6444
6445 if (SUCCEEDED(rc))
6446 {
6447 /* inform the direct session if any */
6448 alock.release();
6449 onStorageControllerChange();
6450 }
6451
6452 return rc;
6453}
6454
6455STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6456{
6457 CheckComArgStrNotEmptyOrNull(aName);
6458
6459 AutoCaller autoCaller(this);
6460 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6461
6462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6463
6464 HRESULT rc = checkStateDependency(MutableStateDep);
6465 if (FAILED(rc)) return rc;
6466
6467 ComObjPtr<StorageController> ctrl;
6468 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6469 if (FAILED(rc)) return rc;
6470
6471 {
6472 /* find all attached devices to the appropriate storage controller and detach them all */
6473 // make a temporary list because detachDevice invalidates iterators into
6474 // mMediaData->mAttachments
6475 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6476
6477 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6478 it != llAttachments2.end();
6479 ++it)
6480 {
6481 MediumAttachment *pAttachTemp = *it;
6482
6483 AutoCaller localAutoCaller(pAttachTemp);
6484 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6485
6486 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6487
6488 if (pAttachTemp->getControllerName() == aName)
6489 {
6490 rc = detachDevice(pAttachTemp, alock, NULL);
6491 if (FAILED(rc)) return rc;
6492 }
6493 }
6494 }
6495
6496 /* We can remove it now. */
6497 setModified(IsModified_Storage);
6498 mStorageControllers.backup();
6499
6500 ctrl->unshare();
6501
6502 mStorageControllers->remove(ctrl);
6503
6504 /* inform the direct session if any */
6505 alock.release();
6506 onStorageControllerChange();
6507
6508 return S_OK;
6509}
6510
6511STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6512 ULONG *puOriginX,
6513 ULONG *puOriginY,
6514 ULONG *puWidth,
6515 ULONG *puHeight,
6516 BOOL *pfEnabled)
6517{
6518 LogFlowThisFunc(("\n"));
6519
6520 CheckComArgNotNull(puOriginX);
6521 CheckComArgNotNull(puOriginY);
6522 CheckComArgNotNull(puWidth);
6523 CheckComArgNotNull(puHeight);
6524 CheckComArgNotNull(pfEnabled);
6525
6526 uint32_t u32OriginX= 0;
6527 uint32_t u32OriginY= 0;
6528 uint32_t u32Width = 0;
6529 uint32_t u32Height = 0;
6530 uint16_t u16Flags = 0;
6531
6532 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6533 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6534 if (RT_FAILURE(vrc))
6535 {
6536#ifdef RT_OS_WINDOWS
6537 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6538 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6539 * So just assign fEnable to TRUE again.
6540 * The right fix would be to change GUI API wrappers to make sure that parameters
6541 * are changed only if API succeeds.
6542 */
6543 *pfEnabled = TRUE;
6544#endif
6545 return setError(VBOX_E_IPRT_ERROR,
6546 tr("Saved guest size is not available (%Rrc)"),
6547 vrc);
6548 }
6549
6550 *puOriginX = u32OriginX;
6551 *puOriginY = u32OriginY;
6552 *puWidth = u32Width;
6553 *puHeight = u32Height;
6554 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6555
6556 return S_OK;
6557}
6558
6559STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6560{
6561 LogFlowThisFunc(("\n"));
6562
6563 CheckComArgNotNull(aSize);
6564 CheckComArgNotNull(aWidth);
6565 CheckComArgNotNull(aHeight);
6566
6567 if (aScreenId != 0)
6568 return E_NOTIMPL;
6569
6570 AutoCaller autoCaller(this);
6571 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6572
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 uint8_t *pu8Data = NULL;
6576 uint32_t cbData = 0;
6577 uint32_t u32Width = 0;
6578 uint32_t u32Height = 0;
6579
6580 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6581
6582 if (RT_FAILURE(vrc))
6583 return setError(VBOX_E_IPRT_ERROR,
6584 tr("Saved screenshot data is not available (%Rrc)"),
6585 vrc);
6586
6587 *aSize = cbData;
6588 *aWidth = u32Width;
6589 *aHeight = u32Height;
6590
6591 freeSavedDisplayScreenshot(pu8Data);
6592
6593 return S_OK;
6594}
6595
6596STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6597{
6598 LogFlowThisFunc(("\n"));
6599
6600 CheckComArgNotNull(aWidth);
6601 CheckComArgNotNull(aHeight);
6602 CheckComArgOutSafeArrayPointerValid(aData);
6603
6604 if (aScreenId != 0)
6605 return E_NOTIMPL;
6606
6607 AutoCaller autoCaller(this);
6608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6609
6610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6611
6612 uint8_t *pu8Data = NULL;
6613 uint32_t cbData = 0;
6614 uint32_t u32Width = 0;
6615 uint32_t u32Height = 0;
6616
6617 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6618
6619 if (RT_FAILURE(vrc))
6620 return setError(VBOX_E_IPRT_ERROR,
6621 tr("Saved screenshot data is not available (%Rrc)"),
6622 vrc);
6623
6624 *aWidth = u32Width;
6625 *aHeight = u32Height;
6626
6627 com::SafeArray<BYTE> bitmap(cbData);
6628 /* Convert pixels to format expected by the API caller. */
6629 if (aBGR)
6630 {
6631 /* [0] B, [1] G, [2] R, [3] A. */
6632 for (unsigned i = 0; i < cbData; i += 4)
6633 {
6634 bitmap[i] = pu8Data[i];
6635 bitmap[i + 1] = pu8Data[i + 1];
6636 bitmap[i + 2] = pu8Data[i + 2];
6637 bitmap[i + 3] = 0xff;
6638 }
6639 }
6640 else
6641 {
6642 /* [0] R, [1] G, [2] B, [3] A. */
6643 for (unsigned i = 0; i < cbData; i += 4)
6644 {
6645 bitmap[i] = pu8Data[i + 2];
6646 bitmap[i + 1] = pu8Data[i + 1];
6647 bitmap[i + 2] = pu8Data[i];
6648 bitmap[i + 3] = 0xff;
6649 }
6650 }
6651 bitmap.detachTo(ComSafeArrayOutArg(aData));
6652
6653 freeSavedDisplayScreenshot(pu8Data);
6654
6655 return S_OK;
6656}
6657
6658
6659STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6660{
6661 LogFlowThisFunc(("\n"));
6662
6663 CheckComArgNotNull(aWidth);
6664 CheckComArgNotNull(aHeight);
6665 CheckComArgOutSafeArrayPointerValid(aData);
6666
6667 if (aScreenId != 0)
6668 return E_NOTIMPL;
6669
6670 AutoCaller autoCaller(this);
6671 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6672
6673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6674
6675 uint8_t *pu8Data = NULL;
6676 uint32_t cbData = 0;
6677 uint32_t u32Width = 0;
6678 uint32_t u32Height = 0;
6679
6680 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6681
6682 if (RT_FAILURE(vrc))
6683 return setError(VBOX_E_IPRT_ERROR,
6684 tr("Saved screenshot data is not available (%Rrc)"),
6685 vrc);
6686
6687 *aWidth = u32Width;
6688 *aHeight = u32Height;
6689
6690 HRESULT rc = S_OK;
6691 uint8_t *pu8PNG = NULL;
6692 uint32_t cbPNG = 0;
6693 uint32_t cxPNG = 0;
6694 uint32_t cyPNG = 0;
6695
6696 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6697
6698 if (RT_SUCCESS(vrc))
6699 {
6700 com::SafeArray<BYTE> screenData(cbPNG);
6701 screenData.initFrom(pu8PNG, cbPNG);
6702 if (pu8PNG)
6703 RTMemFree(pu8PNG);
6704 screenData.detachTo(ComSafeArrayOutArg(aData));
6705 }
6706 else
6707 {
6708 if (pu8PNG)
6709 RTMemFree(pu8PNG);
6710 return setError(VBOX_E_IPRT_ERROR,
6711 tr("Could not convert screenshot to PNG (%Rrc)"),
6712 vrc);
6713 }
6714
6715 freeSavedDisplayScreenshot(pu8Data);
6716
6717 return rc;
6718}
6719
6720STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6721{
6722 LogFlowThisFunc(("\n"));
6723
6724 CheckComArgNotNull(aSize);
6725 CheckComArgNotNull(aWidth);
6726 CheckComArgNotNull(aHeight);
6727
6728 if (aScreenId != 0)
6729 return E_NOTIMPL;
6730
6731 AutoCaller autoCaller(this);
6732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6733
6734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6735
6736 uint8_t *pu8Data = NULL;
6737 uint32_t cbData = 0;
6738 uint32_t u32Width = 0;
6739 uint32_t u32Height = 0;
6740
6741 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6742
6743 if (RT_FAILURE(vrc))
6744 return setError(VBOX_E_IPRT_ERROR,
6745 tr("Saved screenshot data is not available (%Rrc)"),
6746 vrc);
6747
6748 *aSize = cbData;
6749 *aWidth = u32Width;
6750 *aHeight = u32Height;
6751
6752 freeSavedDisplayScreenshot(pu8Data);
6753
6754 return S_OK;
6755}
6756
6757STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6758{
6759 LogFlowThisFunc(("\n"));
6760
6761 CheckComArgNotNull(aWidth);
6762 CheckComArgNotNull(aHeight);
6763 CheckComArgOutSafeArrayPointerValid(aData);
6764
6765 if (aScreenId != 0)
6766 return E_NOTIMPL;
6767
6768 AutoCaller autoCaller(this);
6769 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6770
6771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6772
6773 uint8_t *pu8Data = NULL;
6774 uint32_t cbData = 0;
6775 uint32_t u32Width = 0;
6776 uint32_t u32Height = 0;
6777
6778 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6779
6780 if (RT_FAILURE(vrc))
6781 return setError(VBOX_E_IPRT_ERROR,
6782 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6783 vrc);
6784
6785 *aWidth = u32Width;
6786 *aHeight = u32Height;
6787
6788 com::SafeArray<BYTE> png(cbData);
6789 png.initFrom(pu8Data, cbData);
6790 png.detachTo(ComSafeArrayOutArg(aData));
6791
6792 freeSavedDisplayScreenshot(pu8Data);
6793
6794 return S_OK;
6795}
6796
6797STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6798{
6799 HRESULT rc = S_OK;
6800 LogFlowThisFunc(("\n"));
6801
6802 AutoCaller autoCaller(this);
6803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6804
6805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6806
6807 if (!mHWData->mCPUHotPlugEnabled)
6808 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6809
6810 if (aCpu >= mHWData->mCPUCount)
6811 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6812
6813 if (mHWData->mCPUAttached[aCpu])
6814 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6815
6816 alock.release();
6817 rc = onCPUChange(aCpu, false);
6818 alock.acquire();
6819 if (FAILED(rc)) return rc;
6820
6821 setModified(IsModified_MachineData);
6822 mHWData.backup();
6823 mHWData->mCPUAttached[aCpu] = true;
6824
6825 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6826 if (Global::IsOnline(mData->mMachineState))
6827 saveSettings(NULL);
6828
6829 return S_OK;
6830}
6831
6832STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6833{
6834 HRESULT rc = S_OK;
6835 LogFlowThisFunc(("\n"));
6836
6837 AutoCaller autoCaller(this);
6838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6839
6840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6841
6842 if (!mHWData->mCPUHotPlugEnabled)
6843 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6844
6845 if (aCpu >= SchemaDefs::MaxCPUCount)
6846 return setError(E_INVALIDARG,
6847 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6848 SchemaDefs::MaxCPUCount);
6849
6850 if (!mHWData->mCPUAttached[aCpu])
6851 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6852
6853 /* CPU 0 can't be detached */
6854 if (aCpu == 0)
6855 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6856
6857 alock.release();
6858 rc = onCPUChange(aCpu, true);
6859 alock.acquire();
6860 if (FAILED(rc)) return rc;
6861
6862 setModified(IsModified_MachineData);
6863 mHWData.backup();
6864 mHWData->mCPUAttached[aCpu] = false;
6865
6866 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6867 if (Global::IsOnline(mData->mMachineState))
6868 saveSettings(NULL);
6869
6870 return S_OK;
6871}
6872
6873STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6874{
6875 LogFlowThisFunc(("\n"));
6876
6877 CheckComArgNotNull(aCpuAttached);
6878
6879 *aCpuAttached = false;
6880
6881 AutoCaller autoCaller(this);
6882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6883
6884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6885
6886 /* If hotplug is enabled the CPU is always enabled. */
6887 if (!mHWData->mCPUHotPlugEnabled)
6888 {
6889 if (aCpu < mHWData->mCPUCount)
6890 *aCpuAttached = true;
6891 }
6892 else
6893 {
6894 if (aCpu < SchemaDefs::MaxCPUCount)
6895 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6896 }
6897
6898 return S_OK;
6899}
6900
6901STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6902{
6903 CheckComArgOutPointerValid(aName);
6904
6905 AutoCaller autoCaller(this);
6906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6907
6908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6909
6910 Utf8Str log = queryLogFilename(aIdx);
6911 if (!RTFileExists(log.c_str()))
6912 log.setNull();
6913 log.cloneTo(aName);
6914
6915 return S_OK;
6916}
6917
6918STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6919{
6920 LogFlowThisFunc(("\n"));
6921 CheckComArgOutSafeArrayPointerValid(aData);
6922 if (aSize < 0)
6923 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6924
6925 AutoCaller autoCaller(this);
6926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6927
6928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6929
6930 HRESULT rc = S_OK;
6931 Utf8Str log = queryLogFilename(aIdx);
6932
6933 /* do not unnecessarily hold the lock while doing something which does
6934 * not need the lock and potentially takes a long time. */
6935 alock.release();
6936
6937 /* Limit the chunk size to 32K for now, as that gives better performance
6938 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6939 * One byte expands to approx. 25 bytes of breathtaking XML. */
6940 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6941 com::SafeArray<BYTE> logData(cbData);
6942
6943 RTFILE LogFile;
6944 int vrc = RTFileOpen(&LogFile, log.c_str(),
6945 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6946 if (RT_SUCCESS(vrc))
6947 {
6948 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6949 if (RT_SUCCESS(vrc))
6950 logData.resize(cbData);
6951 else
6952 rc = setError(VBOX_E_IPRT_ERROR,
6953 tr("Could not read log file '%s' (%Rrc)"),
6954 log.c_str(), vrc);
6955 RTFileClose(LogFile);
6956 }
6957 else
6958 rc = setError(VBOX_E_IPRT_ERROR,
6959 tr("Could not open log file '%s' (%Rrc)"),
6960 log.c_str(), vrc);
6961
6962 if (FAILED(rc))
6963 logData.resize(0);
6964 logData.detachTo(ComSafeArrayOutArg(aData));
6965
6966 return rc;
6967}
6968
6969
6970/**
6971 * Currently this method doesn't attach device to the running VM,
6972 * just makes sure it's plugged on next VM start.
6973 */
6974STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6975{
6976 AutoCaller autoCaller(this);
6977 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6978
6979 // lock scope
6980 {
6981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6982
6983 HRESULT rc = checkStateDependency(MutableStateDep);
6984 if (FAILED(rc)) return rc;
6985
6986 ChipsetType_T aChipset = ChipsetType_PIIX3;
6987 COMGETTER(ChipsetType)(&aChipset);
6988
6989 if (aChipset != ChipsetType_ICH9)
6990 {
6991 return setError(E_INVALIDARG,
6992 tr("Host PCI attachment only supported with ICH9 chipset"));
6993 }
6994
6995 // check if device with this host PCI address already attached
6996 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6997 it != mHWData->mPCIDeviceAssignments.end();
6998 ++it)
6999 {
7000 LONG iHostAddress = -1;
7001 ComPtr<PCIDeviceAttachment> pAttach;
7002 pAttach = *it;
7003 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7004 if (iHostAddress == hostAddress)
7005 return setError(E_INVALIDARG,
7006 tr("Device with host PCI address already attached to this VM"));
7007 }
7008
7009 ComObjPtr<PCIDeviceAttachment> pda;
7010 char name[32];
7011
7012 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7013 Bstr bname(name);
7014 pda.createObject();
7015 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7016 setModified(IsModified_MachineData);
7017 mHWData.backup();
7018 mHWData->mPCIDeviceAssignments.push_back(pda);
7019 }
7020
7021 return S_OK;
7022}
7023
7024/**
7025 * Currently this method doesn't detach device from the running VM,
7026 * just makes sure it's not plugged on next VM start.
7027 */
7028STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7029{
7030 AutoCaller autoCaller(this);
7031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7032
7033 ComObjPtr<PCIDeviceAttachment> pAttach;
7034 bool fRemoved = false;
7035 HRESULT rc;
7036
7037 // lock scope
7038 {
7039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 rc = checkStateDependency(MutableStateDep);
7042 if (FAILED(rc)) return rc;
7043
7044 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7045 it != mHWData->mPCIDeviceAssignments.end();
7046 ++it)
7047 {
7048 LONG iHostAddress = -1;
7049 pAttach = *it;
7050 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7051 if (iHostAddress != -1 && iHostAddress == hostAddress)
7052 {
7053 setModified(IsModified_MachineData);
7054 mHWData.backup();
7055 mHWData->mPCIDeviceAssignments.remove(pAttach);
7056 fRemoved = true;
7057 break;
7058 }
7059 }
7060 }
7061
7062
7063 /* Fire event outside of the lock */
7064 if (fRemoved)
7065 {
7066 Assert(!pAttach.isNull());
7067 ComPtr<IEventSource> es;
7068 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7069 Assert(SUCCEEDED(rc));
7070 Bstr mid;
7071 rc = this->COMGETTER(Id)(mid.asOutParam());
7072 Assert(SUCCEEDED(rc));
7073 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7074 }
7075
7076 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7077 tr("No host PCI device %08x attached"),
7078 hostAddress
7079 );
7080}
7081
7082STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7083{
7084 CheckComArgOutSafeArrayPointerValid(aAssignments);
7085
7086 AutoCaller autoCaller(this);
7087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7088
7089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7092 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7093
7094 return S_OK;
7095}
7096
7097STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7098{
7099 CheckComArgOutPointerValid(aBandwidthControl);
7100
7101 AutoCaller autoCaller(this);
7102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7103
7104 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7105
7106 return S_OK;
7107}
7108
7109STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7110{
7111 CheckComArgOutPointerValid(pfEnabled);
7112 AutoCaller autoCaller(this);
7113 HRESULT hrc = autoCaller.rc();
7114 if (SUCCEEDED(hrc))
7115 {
7116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7117 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7118 }
7119 return hrc;
7120}
7121
7122STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7123{
7124 AutoCaller autoCaller(this);
7125 HRESULT hrc = autoCaller.rc();
7126 if (SUCCEEDED(hrc))
7127 {
7128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7129 hrc = checkStateDependency(MutableStateDep);
7130 if (SUCCEEDED(hrc))
7131 {
7132 hrc = mHWData.backupEx();
7133 if (SUCCEEDED(hrc))
7134 {
7135 setModified(IsModified_MachineData);
7136 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7137 }
7138 }
7139 }
7140 return hrc;
7141}
7142
7143STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7144{
7145 CheckComArgOutPointerValid(pbstrConfig);
7146 AutoCaller autoCaller(this);
7147 HRESULT hrc = autoCaller.rc();
7148 if (SUCCEEDED(hrc))
7149 {
7150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7151 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7152 }
7153 return hrc;
7154}
7155
7156STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7157{
7158 CheckComArgStr(bstrConfig);
7159 AutoCaller autoCaller(this);
7160 HRESULT hrc = autoCaller.rc();
7161 if (SUCCEEDED(hrc))
7162 {
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164 hrc = checkStateDependency(MutableStateDep);
7165 if (SUCCEEDED(hrc))
7166 {
7167 hrc = mHWData.backupEx();
7168 if (SUCCEEDED(hrc))
7169 {
7170 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7171 if (SUCCEEDED(hrc))
7172 setModified(IsModified_MachineData);
7173 }
7174 }
7175 }
7176 return hrc;
7177
7178}
7179
7180STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7181{
7182 CheckComArgOutPointerValid(pfAllow);
7183 AutoCaller autoCaller(this);
7184 HRESULT hrc = autoCaller.rc();
7185 if (SUCCEEDED(hrc))
7186 {
7187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7188 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7189 }
7190 return hrc;
7191}
7192
7193STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7194{
7195 AutoCaller autoCaller(this);
7196 HRESULT hrc = autoCaller.rc();
7197 if (SUCCEEDED(hrc))
7198 {
7199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7200 hrc = checkStateDependency(MutableStateDep);
7201 if (SUCCEEDED(hrc))
7202 {
7203 hrc = mHWData.backupEx();
7204 if (SUCCEEDED(hrc))
7205 {
7206 setModified(IsModified_MachineData);
7207 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7208 }
7209 }
7210 }
7211 return hrc;
7212}
7213
7214STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7215{
7216 CheckComArgOutPointerValid(pfEnabled);
7217 AutoCaller autoCaller(this);
7218 HRESULT hrc = autoCaller.rc();
7219 if (SUCCEEDED(hrc))
7220 {
7221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7222 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7223 }
7224 return hrc;
7225}
7226
7227STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7228{
7229 AutoCaller autoCaller(this);
7230 HRESULT hrc = autoCaller.rc();
7231 if (SUCCEEDED(hrc))
7232 {
7233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7234 hrc = checkStateDependency(MutableStateDep);
7235 if ( SUCCEEDED(hrc)
7236 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7237 {
7238 AutostartDb *autostartDb = mParent->getAutostartDb();
7239 int vrc;
7240
7241 if (fEnabled)
7242 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7243 else
7244 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7245
7246 if (RT_SUCCESS(vrc))
7247 {
7248 hrc = mHWData.backupEx();
7249 if (SUCCEEDED(hrc))
7250 {
7251 setModified(IsModified_MachineData);
7252 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7253 }
7254 }
7255 else if (vrc == VERR_NOT_SUPPORTED)
7256 hrc = setError(VBOX_E_NOT_SUPPORTED,
7257 tr("The VM autostart feature is not supported on this platform"));
7258 else if (vrc == VERR_PATH_NOT_FOUND)
7259 hrc = setError(E_FAIL,
7260 tr("The path to the autostart database is not set"));
7261 else
7262 hrc = setError(E_UNEXPECTED,
7263 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7264 fEnabled ? "Adding" : "Removing",
7265 mUserData->s.strName.c_str(), vrc);
7266 }
7267 }
7268 return hrc;
7269}
7270
7271STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7272{
7273 CheckComArgOutPointerValid(puDelay);
7274 AutoCaller autoCaller(this);
7275 HRESULT hrc = autoCaller.rc();
7276 if (SUCCEEDED(hrc))
7277 {
7278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7279 *puDelay = mHWData->mAutostart.uAutostartDelay;
7280 }
7281 return hrc;
7282}
7283
7284STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7285{
7286 AutoCaller autoCaller(this);
7287 HRESULT hrc = autoCaller.rc();
7288 if (SUCCEEDED(hrc))
7289 {
7290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7291 hrc = checkStateDependency(MutableStateDep);
7292 if (SUCCEEDED(hrc))
7293 {
7294 hrc = mHWData.backupEx();
7295 if (SUCCEEDED(hrc))
7296 {
7297 setModified(IsModified_MachineData);
7298 mHWData->mAutostart.uAutostartDelay = uDelay;
7299 }
7300 }
7301 }
7302 return hrc;
7303}
7304
7305STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7306{
7307 CheckComArgOutPointerValid(penmAutostopType);
7308 AutoCaller autoCaller(this);
7309 HRESULT hrc = autoCaller.rc();
7310 if (SUCCEEDED(hrc))
7311 {
7312 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7313 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7314 }
7315 return hrc;
7316}
7317
7318STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7319{
7320 AutoCaller autoCaller(this);
7321 HRESULT hrc = autoCaller.rc();
7322 if (SUCCEEDED(hrc))
7323 {
7324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7325 hrc = checkStateDependency(MutableStateDep);
7326 if ( SUCCEEDED(hrc)
7327 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7328 {
7329 AutostartDb *autostartDb = mParent->getAutostartDb();
7330 int vrc;
7331
7332 if (enmAutostopType != AutostopType_Disabled)
7333 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7334 else
7335 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7336
7337 if (RT_SUCCESS(vrc))
7338 {
7339 hrc = mHWData.backupEx();
7340 if (SUCCEEDED(hrc))
7341 {
7342 setModified(IsModified_MachineData);
7343 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7344 }
7345 }
7346 else if (vrc == VERR_NOT_SUPPORTED)
7347 hrc = setError(VBOX_E_NOT_SUPPORTED,
7348 tr("The VM autostop feature is not supported on this platform"));
7349 else if (vrc == VERR_PATH_NOT_FOUND)
7350 hrc = setError(E_FAIL,
7351 tr("The path to the autostart database is not set"));
7352 else
7353 hrc = setError(E_UNEXPECTED,
7354 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7355 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7356 mUserData->s.strName.c_str(), vrc);
7357 }
7358 }
7359 return hrc;
7360}
7361
7362STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7363{
7364 CheckComArgOutPointerValid(aDefaultFrontend);
7365 AutoCaller autoCaller(this);
7366 HRESULT hrc = autoCaller.rc();
7367 if (SUCCEEDED(hrc))
7368 {
7369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7370 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7371 }
7372 return hrc;
7373}
7374
7375STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7376{
7377 CheckComArgStr(aDefaultFrontend);
7378 AutoCaller autoCaller(this);
7379 HRESULT hrc = autoCaller.rc();
7380 if (SUCCEEDED(hrc))
7381 {
7382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7383 hrc = checkStateDependency(MutableOrSavedStateDep);
7384 if (SUCCEEDED(hrc))
7385 {
7386 hrc = mHWData.backupEx();
7387 if (SUCCEEDED(hrc))
7388 {
7389 setModified(IsModified_MachineData);
7390 mHWData->mDefaultFrontend = aDefaultFrontend;
7391 }
7392 }
7393 }
7394 return hrc;
7395}
7396
7397STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7398{
7399 CheckComArgSafeArrayNotNull(aIcon);
7400 CheckComArgOutSafeArrayPointerValid(aIcon);
7401 AutoCaller autoCaller(this);
7402 HRESULT hrc = autoCaller.rc();
7403 if (SUCCEEDED(hrc))
7404 {
7405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7406 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7407 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7408 icon.detachTo(ComSafeArrayOutArg(aIcon));
7409 }
7410 return hrc;
7411}
7412
7413STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7414{
7415 CheckComArgSafeArrayNotNull(aIcon);
7416 AutoCaller autoCaller(this);
7417 HRESULT hrc = autoCaller.rc();
7418 if (SUCCEEDED(hrc))
7419 {
7420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7421 hrc = checkStateDependency(MutableOrSavedStateDep);
7422 if (SUCCEEDED(hrc))
7423 {
7424 setModified(IsModified_MachineData);
7425 mUserData.backup();
7426 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7427 mUserData->mIcon.clear();
7428 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7429 }
7430 }
7431 return hrc;
7432}
7433
7434STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7435{
7436 LogFlowFuncEnter();
7437
7438 CheckComArgNotNull(pTarget);
7439 CheckComArgOutPointerValid(pProgress);
7440
7441 /* Convert the options. */
7442 RTCList<CloneOptions_T> optList;
7443 if (options != NULL)
7444 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7445
7446 if (optList.contains(CloneOptions_Link))
7447 {
7448 if (!isSnapshotMachine())
7449 return setError(E_INVALIDARG,
7450 tr("Linked clone can only be created from a snapshot"));
7451 if (mode != CloneMode_MachineState)
7452 return setError(E_INVALIDARG,
7453 tr("Linked clone can only be created for a single machine state"));
7454 }
7455 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7456
7457 AutoCaller autoCaller(this);
7458 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7459
7460
7461 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7462
7463 HRESULT rc = pWorker->start(pProgress);
7464
7465 LogFlowFuncLeave();
7466
7467 return rc;
7468}
7469
7470// public methods for internal purposes
7471/////////////////////////////////////////////////////////////////////////////
7472
7473/**
7474 * Adds the given IsModified_* flag to the dirty flags of the machine.
7475 * This must be called either during loadSettings or under the machine write lock.
7476 * @param fl
7477 */
7478void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7479{
7480 mData->flModifications |= fl;
7481 if (fAllowStateModification && isStateModificationAllowed())
7482 mData->mCurrentStateModified = true;
7483}
7484
7485/**
7486 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7487 * care of the write locking.
7488 *
7489 * @param fModifications The flag to add.
7490 */
7491void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7492{
7493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7494 setModified(fModification, fAllowStateModification);
7495}
7496
7497/**
7498 * Saves the registry entry of this machine to the given configuration node.
7499 *
7500 * @param aEntryNode Node to save the registry entry to.
7501 *
7502 * @note locks this object for reading.
7503 */
7504HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7505{
7506 AutoLimitedCaller autoCaller(this);
7507 AssertComRCReturnRC(autoCaller.rc());
7508
7509 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7510
7511 data.uuid = mData->mUuid;
7512 data.strSettingsFile = mData->m_strConfigFile;
7513
7514 return S_OK;
7515}
7516
7517/**
7518 * Calculates the absolute path of the given path taking the directory of the
7519 * machine settings file as the current directory.
7520 *
7521 * @param aPath Path to calculate the absolute path for.
7522 * @param aResult Where to put the result (used only on success, can be the
7523 * same Utf8Str instance as passed in @a aPath).
7524 * @return IPRT result.
7525 *
7526 * @note Locks this object for reading.
7527 */
7528int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7529{
7530 AutoCaller autoCaller(this);
7531 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7532
7533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7534
7535 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7536
7537 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7538
7539 strSettingsDir.stripFilename();
7540 char folder[RTPATH_MAX];
7541 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7542 if (RT_SUCCESS(vrc))
7543 aResult = folder;
7544
7545 return vrc;
7546}
7547
7548/**
7549 * Copies strSource to strTarget, making it relative to the machine folder
7550 * if it is a subdirectory thereof, or simply copying it otherwise.
7551 *
7552 * @param strSource Path to evaluate and copy.
7553 * @param strTarget Buffer to receive target path.
7554 *
7555 * @note Locks this object for reading.
7556 */
7557void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7558 Utf8Str &strTarget)
7559{
7560 AutoCaller autoCaller(this);
7561 AssertComRCReturn(autoCaller.rc(), (void)0);
7562
7563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7564
7565 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7566 // use strTarget as a temporary buffer to hold the machine settings dir
7567 strTarget = mData->m_strConfigFileFull;
7568 strTarget.stripFilename();
7569 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7570 {
7571 // is relative: then append what's left
7572 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7573 // for empty paths (only possible for subdirs) use "." to avoid
7574 // triggering default settings for not present config attributes.
7575 if (strTarget.isEmpty())
7576 strTarget = ".";
7577 }
7578 else
7579 // is not relative: then overwrite
7580 strTarget = strSource;
7581}
7582
7583/**
7584 * Returns the full path to the machine's log folder in the
7585 * \a aLogFolder argument.
7586 */
7587void Machine::getLogFolder(Utf8Str &aLogFolder)
7588{
7589 AutoCaller autoCaller(this);
7590 AssertComRCReturnVoid(autoCaller.rc());
7591
7592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7593
7594 char szTmp[RTPATH_MAX];
7595 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7596 if (RT_SUCCESS(vrc))
7597 {
7598 if (szTmp[0] && !mUserData.isNull())
7599 {
7600 char szTmp2[RTPATH_MAX];
7601 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7602 if (RT_SUCCESS(vrc))
7603 aLogFolder = BstrFmt("%s%c%s",
7604 szTmp2,
7605 RTPATH_DELIMITER,
7606 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7607 }
7608 else
7609 vrc = VERR_PATH_IS_RELATIVE;
7610 }
7611
7612 if (RT_FAILURE(vrc))
7613 {
7614 // fallback if VBOX_USER_LOGHOME is not set or invalid
7615 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7616 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7617 aLogFolder.append(RTPATH_DELIMITER);
7618 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7619 }
7620}
7621
7622/**
7623 * Returns the full path to the machine's log file for an given index.
7624 */
7625Utf8Str Machine::queryLogFilename(ULONG idx)
7626{
7627 Utf8Str logFolder;
7628 getLogFolder(logFolder);
7629 Assert(logFolder.length());
7630 Utf8Str log;
7631 if (idx == 0)
7632 log = Utf8StrFmt("%s%cVBox.log",
7633 logFolder.c_str(), RTPATH_DELIMITER);
7634 else
7635 log = Utf8StrFmt("%s%cVBox.log.%d",
7636 logFolder.c_str(), RTPATH_DELIMITER, idx);
7637 return log;
7638}
7639
7640/**
7641 * Composes a unique saved state filename based on the current system time. The filename is
7642 * granular to the second so this will work so long as no more than one snapshot is taken on
7643 * a machine per second.
7644 *
7645 * Before version 4.1, we used this formula for saved state files:
7646 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7647 * which no longer works because saved state files can now be shared between the saved state of the
7648 * "saved" machine and an online snapshot, and the following would cause problems:
7649 * 1) save machine
7650 * 2) create online snapshot from that machine state --> reusing saved state file
7651 * 3) save machine again --> filename would be reused, breaking the online snapshot
7652 *
7653 * So instead we now use a timestamp.
7654 *
7655 * @param str
7656 */
7657void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7658{
7659 AutoCaller autoCaller(this);
7660 AssertComRCReturnVoid(autoCaller.rc());
7661
7662 {
7663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7664 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7665 }
7666
7667 RTTIMESPEC ts;
7668 RTTimeNow(&ts);
7669 RTTIME time;
7670 RTTimeExplode(&time, &ts);
7671
7672 strStateFilePath += RTPATH_DELIMITER;
7673 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7674 time.i32Year, time.u8Month, time.u8MonthDay,
7675 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7676}
7677
7678/**
7679 * Returns the full path to the default video capture file.
7680 */
7681void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7682{
7683 AutoCaller autoCaller(this);
7684 AssertComRCReturnVoid(autoCaller.rc());
7685
7686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7687
7688 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7689 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7690 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7691}
7692
7693/**
7694 * @note Locks this object for writing, calls the client process
7695 * (inside the lock).
7696 */
7697HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7698 const Utf8Str &strFrontend,
7699 const Utf8Str &strEnvironment,
7700 ProgressProxy *aProgress)
7701{
7702 LogFlowThisFuncEnter();
7703
7704 AssertReturn(aControl, E_FAIL);
7705 AssertReturn(aProgress, E_FAIL);
7706 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7707
7708 AutoCaller autoCaller(this);
7709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7710
7711 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7712
7713 if (!mData->mRegistered)
7714 return setError(E_UNEXPECTED,
7715 tr("The machine '%s' is not registered"),
7716 mUserData->s.strName.c_str());
7717
7718 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7719
7720 if ( mData->mSession.mState == SessionState_Locked
7721 || mData->mSession.mState == SessionState_Spawning
7722 || mData->mSession.mState == SessionState_Unlocking)
7723 return setError(VBOX_E_INVALID_OBJECT_STATE,
7724 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7725 mUserData->s.strName.c_str());
7726
7727 /* may not be busy */
7728 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7729
7730 /* get the path to the executable */
7731 char szPath[RTPATH_MAX];
7732 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7733 size_t sz = strlen(szPath);
7734 szPath[sz++] = RTPATH_DELIMITER;
7735 szPath[sz] = 0;
7736 char *cmd = szPath + sz;
7737 sz = RTPATH_MAX - sz;
7738
7739 int vrc = VINF_SUCCESS;
7740 RTPROCESS pid = NIL_RTPROCESS;
7741
7742 RTENV env = RTENV_DEFAULT;
7743
7744 if (!strEnvironment.isEmpty())
7745 {
7746 char *newEnvStr = NULL;
7747
7748 do
7749 {
7750 /* clone the current environment */
7751 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7752 AssertRCBreakStmt(vrc2, vrc = vrc2);
7753
7754 newEnvStr = RTStrDup(strEnvironment.c_str());
7755 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7756
7757 /* put new variables to the environment
7758 * (ignore empty variable names here since RTEnv API
7759 * intentionally doesn't do that) */
7760 char *var = newEnvStr;
7761 for (char *p = newEnvStr; *p; ++p)
7762 {
7763 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7764 {
7765 *p = '\0';
7766 if (*var)
7767 {
7768 char *val = strchr(var, '=');
7769 if (val)
7770 {
7771 *val++ = '\0';
7772 vrc2 = RTEnvSetEx(env, var, val);
7773 }
7774 else
7775 vrc2 = RTEnvUnsetEx(env, var);
7776 if (RT_FAILURE(vrc2))
7777 break;
7778 }
7779 var = p + 1;
7780 }
7781 }
7782 if (RT_SUCCESS(vrc2) && *var)
7783 vrc2 = RTEnvPutEx(env, var);
7784
7785 AssertRCBreakStmt(vrc2, vrc = vrc2);
7786 }
7787 while (0);
7788
7789 if (newEnvStr != NULL)
7790 RTStrFree(newEnvStr);
7791 }
7792
7793#ifdef VBOX_WITH_QTGUI
7794 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7795 {
7796# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7797 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7798# else
7799 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7800# endif
7801 Assert(sz >= sizeof(VirtualBox_exe));
7802 strcpy(cmd, VirtualBox_exe);
7803
7804 Utf8Str idStr = mData->mUuid.toString();
7805 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7806 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7807 }
7808#else /* !VBOX_WITH_QTGUI */
7809 if (0)
7810 ;
7811#endif /* VBOX_WITH_QTGUI */
7812
7813 else
7814
7815#ifdef VBOX_WITH_VBOXSDL
7816 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7817 {
7818 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7819 Assert(sz >= sizeof(VBoxSDL_exe));
7820 strcpy(cmd, VBoxSDL_exe);
7821
7822 Utf8Str idStr = mData->mUuid.toString();
7823 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7824 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7825 }
7826#else /* !VBOX_WITH_VBOXSDL */
7827 if (0)
7828 ;
7829#endif /* !VBOX_WITH_VBOXSDL */
7830
7831 else
7832
7833#ifdef VBOX_WITH_HEADLESS
7834 if ( strFrontend == "headless"
7835 || strFrontend == "capture"
7836 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7837 )
7838 {
7839 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7840 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7841 * and a VM works even if the server has not been installed.
7842 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7843 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7844 * differently in 4.0 and 3.x.
7845 */
7846 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7847 Assert(sz >= sizeof(VBoxHeadless_exe));
7848 strcpy(cmd, VBoxHeadless_exe);
7849
7850 Utf8Str idStr = mData->mUuid.toString();
7851 /* Leave space for "--capture" arg. */
7852 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7853 "--startvm", idStr.c_str(),
7854 "--vrde", "config",
7855 0, /* For "--capture". */
7856 0 };
7857 if (strFrontend == "capture")
7858 {
7859 unsigned pos = RT_ELEMENTS(args) - 2;
7860 args[pos] = "--capture";
7861 }
7862 vrc = RTProcCreate(szPath, args, env,
7863#ifdef RT_OS_WINDOWS
7864 RTPROC_FLAGS_NO_WINDOW
7865#else
7866 0
7867#endif
7868 , &pid);
7869 }
7870#else /* !VBOX_WITH_HEADLESS */
7871 if (0)
7872 ;
7873#endif /* !VBOX_WITH_HEADLESS */
7874 else
7875 {
7876 RTEnvDestroy(env);
7877 return setError(E_INVALIDARG,
7878 tr("Invalid frontend name: '%s'"),
7879 strFrontend.c_str());
7880 }
7881
7882 RTEnvDestroy(env);
7883
7884 if (RT_FAILURE(vrc))
7885 return setError(VBOX_E_IPRT_ERROR,
7886 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7887 mUserData->s.strName.c_str(), vrc);
7888
7889 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7890
7891 /*
7892 * Note that we don't release the lock here before calling the client,
7893 * because it doesn't need to call us back if called with a NULL argument.
7894 * Releasing the lock here is dangerous because we didn't prepare the
7895 * launch data yet, but the client we've just started may happen to be
7896 * too fast and call openSession() that will fail (because of PID, etc.),
7897 * so that the Machine will never get out of the Spawning session state.
7898 */
7899
7900 /* inform the session that it will be a remote one */
7901 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7902 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7903 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7904
7905 if (FAILED(rc))
7906 {
7907 /* restore the session state */
7908 mData->mSession.mState = SessionState_Unlocked;
7909 /* The failure may occur w/o any error info (from RPC), so provide one */
7910 return setError(VBOX_E_VM_ERROR,
7911 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7912 }
7913
7914 /* attach launch data to the machine */
7915 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7916 mData->mSession.mRemoteControls.push_back(aControl);
7917 mData->mSession.mProgress = aProgress;
7918 mData->mSession.mPID = pid;
7919 mData->mSession.mState = SessionState_Spawning;
7920 mData->mSession.mType = strFrontend;
7921
7922 LogFlowThisFuncLeave();
7923 return S_OK;
7924}
7925
7926/**
7927 * Returns @c true if the given machine has an open direct session and returns
7928 * the session machine instance and additional session data (on some platforms)
7929 * if so.
7930 *
7931 * Note that when the method returns @c false, the arguments remain unchanged.
7932 *
7933 * @param aMachine Session machine object.
7934 * @param aControl Direct session control object (optional).
7935 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7936 *
7937 * @note locks this object for reading.
7938 */
7939#if defined(RT_OS_WINDOWS)
7940bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7941 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7942 HANDLE *aIPCSem /*= NULL*/,
7943 bool aAllowClosing /*= false*/)
7944#elif defined(RT_OS_OS2)
7945bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7946 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7947 HMTX *aIPCSem /*= NULL*/,
7948 bool aAllowClosing /*= false*/)
7949#else
7950bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7951 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7952 bool aAllowClosing /*= false*/)
7953#endif
7954{
7955 AutoLimitedCaller autoCaller(this);
7956 AssertComRCReturn(autoCaller.rc(), false);
7957
7958 /* just return false for inaccessible machines */
7959 if (autoCaller.state() != Ready)
7960 return false;
7961
7962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7963
7964 if ( mData->mSession.mState == SessionState_Locked
7965 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7966 )
7967 {
7968 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7969
7970 aMachine = mData->mSession.mMachine;
7971
7972 if (aControl != NULL)
7973 *aControl = mData->mSession.mDirectControl;
7974
7975#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7976 /* Additional session data */
7977 if (aIPCSem != NULL)
7978 *aIPCSem = aMachine->mIPCSem;
7979#endif
7980 return true;
7981 }
7982
7983 return false;
7984}
7985
7986/**
7987 * Returns @c true if the given machine has an spawning direct session and
7988 * returns and additional session data (on some platforms) if so.
7989 *
7990 * Note that when the method returns @c false, the arguments remain unchanged.
7991 *
7992 * @param aPID PID of the spawned direct session process.
7993 *
7994 * @note locks this object for reading.
7995 */
7996#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7997bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7998#else
7999bool Machine::isSessionSpawning()
8000#endif
8001{
8002 AutoLimitedCaller autoCaller(this);
8003 AssertComRCReturn(autoCaller.rc(), false);
8004
8005 /* just return false for inaccessible machines */
8006 if (autoCaller.state() != Ready)
8007 return false;
8008
8009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8010
8011 if (mData->mSession.mState == SessionState_Spawning)
8012 {
8013#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8014 /* Additional session data */
8015 if (aPID != NULL)
8016 {
8017 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
8018 *aPID = mData->mSession.mPID;
8019 }
8020#endif
8021 return true;
8022 }
8023
8024 return false;
8025}
8026
8027/**
8028 * Called from the client watcher thread to check for unexpected client process
8029 * death during Session_Spawning state (e.g. before it successfully opened a
8030 * direct session).
8031 *
8032 * On Win32 and on OS/2, this method is called only when we've got the
8033 * direct client's process termination notification, so it always returns @c
8034 * true.
8035 *
8036 * On other platforms, this method returns @c true if the client process is
8037 * terminated and @c false if it's still alive.
8038 *
8039 * @note Locks this object for writing.
8040 */
8041bool Machine::checkForSpawnFailure()
8042{
8043 AutoCaller autoCaller(this);
8044 if (!autoCaller.isOk())
8045 {
8046 /* nothing to do */
8047 LogFlowThisFunc(("Already uninitialized!\n"));
8048 return true;
8049 }
8050
8051 /* VirtualBox::addProcessToReap() needs a write lock */
8052 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8053
8054 if (mData->mSession.mState != SessionState_Spawning)
8055 {
8056 /* nothing to do */
8057 LogFlowThisFunc(("Not spawning any more!\n"));
8058 return true;
8059 }
8060
8061 HRESULT rc = S_OK;
8062
8063#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8064
8065 /* the process was already unexpectedly terminated, we just need to set an
8066 * error and finalize session spawning */
8067 rc = setError(E_FAIL,
8068 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8069 getName().c_str());
8070#else
8071
8072 /* PID not yet initialized, skip check. */
8073 if (mData->mSession.mPID == NIL_RTPROCESS)
8074 return false;
8075
8076 RTPROCSTATUS status;
8077 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8078 &status);
8079
8080 if (vrc != VERR_PROCESS_RUNNING)
8081 {
8082 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8083 rc = setError(E_FAIL,
8084 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8085 getName().c_str(), status.iStatus);
8086 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8087 rc = setError(E_FAIL,
8088 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8089 getName().c_str(), status.iStatus);
8090 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8091 rc = setError(E_FAIL,
8092 tr("The virtual machine '%s' has terminated abnormally"),
8093 getName().c_str(), status.iStatus);
8094 else
8095 rc = setError(E_FAIL,
8096 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8097 getName().c_str(), rc);
8098 }
8099
8100#endif
8101
8102 if (FAILED(rc))
8103 {
8104 /* Close the remote session, remove the remote control from the list
8105 * and reset session state to Closed (@note keep the code in sync with
8106 * the relevant part in checkForSpawnFailure()). */
8107
8108 Assert(mData->mSession.mRemoteControls.size() == 1);
8109 if (mData->mSession.mRemoteControls.size() == 1)
8110 {
8111 ErrorInfoKeeper eik;
8112 mData->mSession.mRemoteControls.front()->Uninitialize();
8113 }
8114
8115 mData->mSession.mRemoteControls.clear();
8116 mData->mSession.mState = SessionState_Unlocked;
8117
8118 /* finalize the progress after setting the state */
8119 if (!mData->mSession.mProgress.isNull())
8120 {
8121 mData->mSession.mProgress->notifyComplete(rc);
8122 mData->mSession.mProgress.setNull();
8123 }
8124
8125 mParent->addProcessToReap(mData->mSession.mPID);
8126 mData->mSession.mPID = NIL_RTPROCESS;
8127
8128 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8129 return true;
8130 }
8131
8132 return false;
8133}
8134
8135/**
8136 * Checks whether the machine can be registered. If so, commits and saves
8137 * all settings.
8138 *
8139 * @note Must be called from mParent's write lock. Locks this object and
8140 * children for writing.
8141 */
8142HRESULT Machine::prepareRegister()
8143{
8144 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8145
8146 AutoLimitedCaller autoCaller(this);
8147 AssertComRCReturnRC(autoCaller.rc());
8148
8149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8150
8151 /* wait for state dependents to drop to zero */
8152 ensureNoStateDependencies();
8153
8154 if (!mData->mAccessible)
8155 return setError(VBOX_E_INVALID_OBJECT_STATE,
8156 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8157 mUserData->s.strName.c_str(),
8158 mData->mUuid.toString().c_str());
8159
8160 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8161
8162 if (mData->mRegistered)
8163 return setError(VBOX_E_INVALID_OBJECT_STATE,
8164 tr("The machine '%s' with UUID {%s} is already registered"),
8165 mUserData->s.strName.c_str(),
8166 mData->mUuid.toString().c_str());
8167
8168 HRESULT rc = S_OK;
8169
8170 // Ensure the settings are saved. If we are going to be registered and
8171 // no config file exists yet, create it by calling saveSettings() too.
8172 if ( (mData->flModifications)
8173 || (!mData->pMachineConfigFile->fileExists())
8174 )
8175 {
8176 rc = saveSettings(NULL);
8177 // no need to check whether VirtualBox.xml needs saving too since
8178 // we can't have a machine XML file rename pending
8179 if (FAILED(rc)) return rc;
8180 }
8181
8182 /* more config checking goes here */
8183
8184 if (SUCCEEDED(rc))
8185 {
8186 /* we may have had implicit modifications we want to fix on success */
8187 commit();
8188
8189 mData->mRegistered = true;
8190 }
8191 else
8192 {
8193 /* we may have had implicit modifications we want to cancel on failure*/
8194 rollback(false /* aNotify */);
8195 }
8196
8197 return rc;
8198}
8199
8200/**
8201 * Increases the number of objects dependent on the machine state or on the
8202 * registered state. Guarantees that these two states will not change at least
8203 * until #releaseStateDependency() is called.
8204 *
8205 * Depending on the @a aDepType value, additional state checks may be made.
8206 * These checks will set extended error info on failure. See
8207 * #checkStateDependency() for more info.
8208 *
8209 * If this method returns a failure, the dependency is not added and the caller
8210 * is not allowed to rely on any particular machine state or registration state
8211 * value and may return the failed result code to the upper level.
8212 *
8213 * @param aDepType Dependency type to add.
8214 * @param aState Current machine state (NULL if not interested).
8215 * @param aRegistered Current registered state (NULL if not interested).
8216 *
8217 * @note Locks this object for writing.
8218 */
8219HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8220 MachineState_T *aState /* = NULL */,
8221 BOOL *aRegistered /* = NULL */)
8222{
8223 AutoCaller autoCaller(this);
8224 AssertComRCReturnRC(autoCaller.rc());
8225
8226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8227
8228 HRESULT rc = checkStateDependency(aDepType);
8229 if (FAILED(rc)) return rc;
8230
8231 {
8232 if (mData->mMachineStateChangePending != 0)
8233 {
8234 /* ensureNoStateDependencies() is waiting for state dependencies to
8235 * drop to zero so don't add more. It may make sense to wait a bit
8236 * and retry before reporting an error (since the pending state
8237 * transition should be really quick) but let's just assert for
8238 * now to see if it ever happens on practice. */
8239
8240 AssertFailed();
8241
8242 return setError(E_ACCESSDENIED,
8243 tr("Machine state change is in progress. Please retry the operation later."));
8244 }
8245
8246 ++mData->mMachineStateDeps;
8247 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8248 }
8249
8250 if (aState)
8251 *aState = mData->mMachineState;
8252 if (aRegistered)
8253 *aRegistered = mData->mRegistered;
8254
8255 return S_OK;
8256}
8257
8258/**
8259 * Decreases the number of objects dependent on the machine state.
8260 * Must always complete the #addStateDependency() call after the state
8261 * dependency is no more necessary.
8262 */
8263void Machine::releaseStateDependency()
8264{
8265 AutoCaller autoCaller(this);
8266 AssertComRCReturnVoid(autoCaller.rc());
8267
8268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8269
8270 /* releaseStateDependency() w/o addStateDependency()? */
8271 AssertReturnVoid(mData->mMachineStateDeps != 0);
8272 -- mData->mMachineStateDeps;
8273
8274 if (mData->mMachineStateDeps == 0)
8275 {
8276 /* inform ensureNoStateDependencies() that there are no more deps */
8277 if (mData->mMachineStateChangePending != 0)
8278 {
8279 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8280 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8281 }
8282 }
8283}
8284
8285Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8286{
8287 /* start with nothing found */
8288 Utf8Str strResult("");
8289
8290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8291
8292 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8293 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8294 // found:
8295 strResult = it->second; // source is a Utf8Str
8296
8297 return strResult;
8298}
8299
8300// protected methods
8301/////////////////////////////////////////////////////////////////////////////
8302
8303/**
8304 * Performs machine state checks based on the @a aDepType value. If a check
8305 * fails, this method will set extended error info, otherwise it will return
8306 * S_OK. It is supposed, that on failure, the caller will immediately return
8307 * the return value of this method to the upper level.
8308 *
8309 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8310 *
8311 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8312 * current state of this machine object allows to change settings of the
8313 * machine (i.e. the machine is not registered, or registered but not running
8314 * and not saved). It is useful to call this method from Machine setters
8315 * before performing any change.
8316 *
8317 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8318 * as for MutableStateDep except that if the machine is saved, S_OK is also
8319 * returned. This is useful in setters which allow changing machine
8320 * properties when it is in the saved state.
8321 *
8322 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8323 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8324 * Aborted).
8325 *
8326 * @param aDepType Dependency type to check.
8327 *
8328 * @note Non Machine based classes should use #addStateDependency() and
8329 * #releaseStateDependency() methods or the smart AutoStateDependency
8330 * template.
8331 *
8332 * @note This method must be called from under this object's read or write
8333 * lock.
8334 */
8335HRESULT Machine::checkStateDependency(StateDependency aDepType)
8336{
8337 switch (aDepType)
8338 {
8339 case AnyStateDep:
8340 {
8341 break;
8342 }
8343 case MutableStateDep:
8344 {
8345 if ( mData->mRegistered
8346 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8347 || ( mData->mMachineState != MachineState_Paused
8348 && mData->mMachineState != MachineState_Running
8349 && mData->mMachineState != MachineState_Aborted
8350 && mData->mMachineState != MachineState_Teleported
8351 && mData->mMachineState != MachineState_PoweredOff
8352 )
8353 )
8354 )
8355 return setError(VBOX_E_INVALID_VM_STATE,
8356 tr("The machine is not mutable (state is %s)"),
8357 Global::stringifyMachineState(mData->mMachineState));
8358 break;
8359 }
8360 case MutableOrSavedStateDep:
8361 {
8362 if ( mData->mRegistered
8363 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8364 || ( mData->mMachineState != MachineState_Paused
8365 && mData->mMachineState != MachineState_Running
8366 && mData->mMachineState != MachineState_Aborted
8367 && mData->mMachineState != MachineState_Teleported
8368 && mData->mMachineState != MachineState_Saved
8369 && mData->mMachineState != MachineState_PoweredOff
8370 )
8371 )
8372 )
8373 return setError(VBOX_E_INVALID_VM_STATE,
8374 tr("The machine is not mutable (state is %s)"),
8375 Global::stringifyMachineState(mData->mMachineState));
8376 break;
8377 }
8378 case OfflineStateDep:
8379 {
8380 if ( mData->mRegistered
8381 && ( !isSessionMachine()
8382 || ( mData->mMachineState != MachineState_PoweredOff
8383 && mData->mMachineState != MachineState_Saved
8384 && mData->mMachineState != MachineState_Aborted
8385 && mData->mMachineState != MachineState_Teleported
8386 )
8387 )
8388 )
8389 return setError(VBOX_E_INVALID_VM_STATE,
8390 tr("The machine is not offline (state is %s)"),
8391 Global::stringifyMachineState(mData->mMachineState));
8392 break;
8393 }
8394 }
8395
8396 return S_OK;
8397}
8398
8399/**
8400 * Helper to initialize all associated child objects and allocate data
8401 * structures.
8402 *
8403 * This method must be called as a part of the object's initialization procedure
8404 * (usually done in the #init() method).
8405 *
8406 * @note Must be called only from #init() or from #registeredInit().
8407 */
8408HRESULT Machine::initDataAndChildObjects()
8409{
8410 AutoCaller autoCaller(this);
8411 AssertComRCReturnRC(autoCaller.rc());
8412 AssertComRCReturn(autoCaller.state() == InInit ||
8413 autoCaller.state() == Limited, E_FAIL);
8414
8415 AssertReturn(!mData->mAccessible, E_FAIL);
8416
8417 /* allocate data structures */
8418 mSSData.allocate();
8419 mUserData.allocate();
8420 mHWData.allocate();
8421 mMediaData.allocate();
8422 mStorageControllers.allocate();
8423
8424 /* initialize mOSTypeId */
8425 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8426
8427 /* create associated BIOS settings object */
8428 unconst(mBIOSSettings).createObject();
8429 mBIOSSettings->init(this);
8430
8431 /* create an associated VRDE object (default is disabled) */
8432 unconst(mVRDEServer).createObject();
8433 mVRDEServer->init(this);
8434
8435 /* create associated serial port objects */
8436 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8437 {
8438 unconst(mSerialPorts[slot]).createObject();
8439 mSerialPorts[slot]->init(this, slot);
8440 }
8441
8442 /* create associated parallel port objects */
8443 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8444 {
8445 unconst(mParallelPorts[slot]).createObject();
8446 mParallelPorts[slot]->init(this, slot);
8447 }
8448
8449 /* create the audio adapter object (always present, default is disabled) */
8450 unconst(mAudioAdapter).createObject();
8451 mAudioAdapter->init(this);
8452
8453 /* create the USB controller object (always present, default is disabled) */
8454 unconst(mUSBController).createObject();
8455 mUSBController->init(this);
8456
8457 /* create associated network adapter objects */
8458 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8459 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8460 {
8461 unconst(mNetworkAdapters[slot]).createObject();
8462 mNetworkAdapters[slot]->init(this, slot);
8463 }
8464
8465 /* create the bandwidth control */
8466 unconst(mBandwidthControl).createObject();
8467 mBandwidthControl->init(this);
8468
8469 return S_OK;
8470}
8471
8472/**
8473 * Helper to uninitialize all associated child objects and to free all data
8474 * structures.
8475 *
8476 * This method must be called as a part of the object's uninitialization
8477 * procedure (usually done in the #uninit() method).
8478 *
8479 * @note Must be called only from #uninit() or from #registeredInit().
8480 */
8481void Machine::uninitDataAndChildObjects()
8482{
8483 AutoCaller autoCaller(this);
8484 AssertComRCReturnVoid(autoCaller.rc());
8485 AssertComRCReturnVoid( autoCaller.state() == InUninit
8486 || autoCaller.state() == Limited);
8487
8488 /* tell all our other child objects we've been uninitialized */
8489 if (mBandwidthControl)
8490 {
8491 mBandwidthControl->uninit();
8492 unconst(mBandwidthControl).setNull();
8493 }
8494
8495 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8496 {
8497 if (mNetworkAdapters[slot])
8498 {
8499 mNetworkAdapters[slot]->uninit();
8500 unconst(mNetworkAdapters[slot]).setNull();
8501 }
8502 }
8503
8504 if (mUSBController)
8505 {
8506 mUSBController->uninit();
8507 unconst(mUSBController).setNull();
8508 }
8509
8510 if (mAudioAdapter)
8511 {
8512 mAudioAdapter->uninit();
8513 unconst(mAudioAdapter).setNull();
8514 }
8515
8516 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8517 {
8518 if (mParallelPorts[slot])
8519 {
8520 mParallelPorts[slot]->uninit();
8521 unconst(mParallelPorts[slot]).setNull();
8522 }
8523 }
8524
8525 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8526 {
8527 if (mSerialPorts[slot])
8528 {
8529 mSerialPorts[slot]->uninit();
8530 unconst(mSerialPorts[slot]).setNull();
8531 }
8532 }
8533
8534 if (mVRDEServer)
8535 {
8536 mVRDEServer->uninit();
8537 unconst(mVRDEServer).setNull();
8538 }
8539
8540 if (mBIOSSettings)
8541 {
8542 mBIOSSettings->uninit();
8543 unconst(mBIOSSettings).setNull();
8544 }
8545
8546 /* Deassociate media (only when a real Machine or a SnapshotMachine
8547 * instance is uninitialized; SessionMachine instances refer to real
8548 * Machine media). This is necessary for a clean re-initialization of
8549 * the VM after successfully re-checking the accessibility state. Note
8550 * that in case of normal Machine or SnapshotMachine uninitialization (as
8551 * a result of unregistering or deleting the snapshot), outdated media
8552 * attachments will already be uninitialized and deleted, so this
8553 * code will not affect them. */
8554 if ( !!mMediaData
8555 && (!isSessionMachine())
8556 )
8557 {
8558 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8559 it != mMediaData->mAttachments.end();
8560 ++it)
8561 {
8562 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8563 if (pMedium.isNull())
8564 continue;
8565 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8566 AssertComRC(rc);
8567 }
8568 }
8569
8570 if (!isSessionMachine() && !isSnapshotMachine())
8571 {
8572 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8573 if (mData->mFirstSnapshot)
8574 {
8575 // snapshots tree is protected by machine write lock; strictly
8576 // this isn't necessary here since we're deleting the entire
8577 // machine, but otherwise we assert in Snapshot::uninit()
8578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8579 mData->mFirstSnapshot->uninit();
8580 mData->mFirstSnapshot.setNull();
8581 }
8582
8583 mData->mCurrentSnapshot.setNull();
8584 }
8585
8586 /* free data structures (the essential mData structure is not freed here
8587 * since it may be still in use) */
8588 mMediaData.free();
8589 mStorageControllers.free();
8590 mHWData.free();
8591 mUserData.free();
8592 mSSData.free();
8593}
8594
8595/**
8596 * Returns a pointer to the Machine object for this machine that acts like a
8597 * parent for complex machine data objects such as shared folders, etc.
8598 *
8599 * For primary Machine objects and for SnapshotMachine objects, returns this
8600 * object's pointer itself. For SessionMachine objects, returns the peer
8601 * (primary) machine pointer.
8602 */
8603Machine* Machine::getMachine()
8604{
8605 if (isSessionMachine())
8606 return (Machine*)mPeer;
8607 return this;
8608}
8609
8610/**
8611 * Makes sure that there are no machine state dependents. If necessary, waits
8612 * for the number of dependents to drop to zero.
8613 *
8614 * Make sure this method is called from under this object's write lock to
8615 * guarantee that no new dependents may be added when this method returns
8616 * control to the caller.
8617 *
8618 * @note Locks this object for writing. The lock will be released while waiting
8619 * (if necessary).
8620 *
8621 * @warning To be used only in methods that change the machine state!
8622 */
8623void Machine::ensureNoStateDependencies()
8624{
8625 AssertReturnVoid(isWriteLockOnCurrentThread());
8626
8627 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8628
8629 /* Wait for all state dependents if necessary */
8630 if (mData->mMachineStateDeps != 0)
8631 {
8632 /* lazy semaphore creation */
8633 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8634 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8635
8636 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8637 mData->mMachineStateDeps));
8638
8639 ++mData->mMachineStateChangePending;
8640
8641 /* reset the semaphore before waiting, the last dependent will signal
8642 * it */
8643 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8644
8645 alock.release();
8646
8647 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8648
8649 alock.acquire();
8650
8651 -- mData->mMachineStateChangePending;
8652 }
8653}
8654
8655/**
8656 * Changes the machine state and informs callbacks.
8657 *
8658 * This method is not intended to fail so it either returns S_OK or asserts (and
8659 * returns a failure).
8660 *
8661 * @note Locks this object for writing.
8662 */
8663HRESULT Machine::setMachineState(MachineState_T aMachineState)
8664{
8665 LogFlowThisFuncEnter();
8666 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8667
8668 AutoCaller autoCaller(this);
8669 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8670
8671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8672
8673 /* wait for state dependents to drop to zero */
8674 ensureNoStateDependencies();
8675
8676 if (mData->mMachineState != aMachineState)
8677 {
8678 mData->mMachineState = aMachineState;
8679
8680 RTTimeNow(&mData->mLastStateChange);
8681
8682 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8683 }
8684
8685 LogFlowThisFuncLeave();
8686 return S_OK;
8687}
8688
8689/**
8690 * Searches for a shared folder with the given logical name
8691 * in the collection of shared folders.
8692 *
8693 * @param aName logical name of the shared folder
8694 * @param aSharedFolder where to return the found object
8695 * @param aSetError whether to set the error info if the folder is
8696 * not found
8697 * @return
8698 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8699 *
8700 * @note
8701 * must be called from under the object's lock!
8702 */
8703HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8704 ComObjPtr<SharedFolder> &aSharedFolder,
8705 bool aSetError /* = false */)
8706{
8707 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8708 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8709 it != mHWData->mSharedFolders.end();
8710 ++it)
8711 {
8712 SharedFolder *pSF = *it;
8713 AutoCaller autoCaller(pSF);
8714 if (pSF->getName() == aName)
8715 {
8716 aSharedFolder = pSF;
8717 rc = S_OK;
8718 break;
8719 }
8720 }
8721
8722 if (aSetError && FAILED(rc))
8723 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8724
8725 return rc;
8726}
8727
8728/**
8729 * Initializes all machine instance data from the given settings structures
8730 * from XML. The exception is the machine UUID which needs special handling
8731 * depending on the caller's use case, so the caller needs to set that herself.
8732 *
8733 * This gets called in several contexts during machine initialization:
8734 *
8735 * -- When machine XML exists on disk already and needs to be loaded into memory,
8736 * for example, from registeredInit() to load all registered machines on
8737 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8738 * attached to the machine should be part of some media registry already.
8739 *
8740 * -- During OVF import, when a machine config has been constructed from an
8741 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8742 * ensure that the media listed as attachments in the config (which have
8743 * been imported from the OVF) receive the correct registry ID.
8744 *
8745 * -- During VM cloning.
8746 *
8747 * @param config Machine settings from XML.
8748 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8749 * @return
8750 */
8751HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8752 const Guid *puuidRegistry)
8753{
8754 // copy name, description, OS type, teleporter, UTC etc.
8755 #define DECODE_STR_MAX _1M
8756 mUserData->s = config.machineUserData;
8757
8758 // Decode the Icon overide data from config userdata and set onto Machine.
8759 const char* pszStr = config.machineUserData.ovIcon.c_str();
8760 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8761 if (cbOut > DECODE_STR_MAX)
8762 return setError(E_FAIL,
8763 tr("Icon Data too long.'%d' > '%d'"),
8764 cbOut,
8765 DECODE_STR_MAX);
8766 com::SafeArray<BYTE> iconByte(cbOut);
8767 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8768 if (FAILED(rc))
8769 return setError(E_FAIL,
8770 tr("Failure to Decode Icon Data. '%s' (%d)"),
8771 pszStr,
8772 rc);
8773 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8774
8775 // look up the object by Id to check it is valid
8776 ComPtr<IGuestOSType> guestOSType;
8777 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8778 guestOSType.asOutParam());
8779 if (FAILED(rc)) return rc;
8780
8781 // stateFile (optional)
8782 if (config.strStateFile.isEmpty())
8783 mSSData->strStateFilePath.setNull();
8784 else
8785 {
8786 Utf8Str stateFilePathFull(config.strStateFile);
8787 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8788 if (RT_FAILURE(vrc))
8789 return setError(E_FAIL,
8790 tr("Invalid saved state file path '%s' (%Rrc)"),
8791 config.strStateFile.c_str(),
8792 vrc);
8793 mSSData->strStateFilePath = stateFilePathFull;
8794 }
8795
8796 // snapshot folder needs special processing so set it again
8797 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8798 if (FAILED(rc)) return rc;
8799
8800 /* Copy the extra data items (Not in any case config is already the same as
8801 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8802 * make sure the extra data map is copied). */
8803 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8804
8805 /* currentStateModified (optional, default is true) */
8806 mData->mCurrentStateModified = config.fCurrentStateModified;
8807
8808 mData->mLastStateChange = config.timeLastStateChange;
8809
8810 /*
8811 * note: all mUserData members must be assigned prior this point because
8812 * we need to commit changes in order to let mUserData be shared by all
8813 * snapshot machine instances.
8814 */
8815 mUserData.commitCopy();
8816
8817 // machine registry, if present (must be loaded before snapshots)
8818 if (config.canHaveOwnMediaRegistry())
8819 {
8820 // determine machine folder
8821 Utf8Str strMachineFolder = getSettingsFileFull();
8822 strMachineFolder.stripFilename();
8823 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8824 config.mediaRegistry,
8825 strMachineFolder);
8826 if (FAILED(rc)) return rc;
8827 }
8828
8829 /* Snapshot node (optional) */
8830 size_t cRootSnapshots;
8831 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8832 {
8833 // there must be only one root snapshot
8834 Assert(cRootSnapshots == 1);
8835
8836 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8837
8838 rc = loadSnapshot(snap,
8839 config.uuidCurrentSnapshot,
8840 NULL); // no parent == first snapshot
8841 if (FAILED(rc)) return rc;
8842 }
8843
8844 // hardware data
8845 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8846 if (FAILED(rc)) return rc;
8847
8848 // load storage controllers
8849 rc = loadStorageControllers(config.storageMachine,
8850 puuidRegistry,
8851 NULL /* puuidSnapshot */);
8852 if (FAILED(rc)) return rc;
8853
8854 /*
8855 * NOTE: the assignment below must be the last thing to do,
8856 * otherwise it will be not possible to change the settings
8857 * somewhere in the code above because all setters will be
8858 * blocked by checkStateDependency(MutableStateDep).
8859 */
8860
8861 /* set the machine state to Aborted or Saved when appropriate */
8862 if (config.fAborted)
8863 {
8864 mSSData->strStateFilePath.setNull();
8865
8866 /* no need to use setMachineState() during init() */
8867 mData->mMachineState = MachineState_Aborted;
8868 }
8869 else if (!mSSData->strStateFilePath.isEmpty())
8870 {
8871 /* no need to use setMachineState() during init() */
8872 mData->mMachineState = MachineState_Saved;
8873 }
8874
8875 // after loading settings, we are no longer different from the XML on disk
8876 mData->flModifications = 0;
8877
8878 return S_OK;
8879}
8880
8881/**
8882 * Recursively loads all snapshots starting from the given.
8883 *
8884 * @param aNode <Snapshot> node.
8885 * @param aCurSnapshotId Current snapshot ID from the settings file.
8886 * @param aParentSnapshot Parent snapshot.
8887 */
8888HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8889 const Guid &aCurSnapshotId,
8890 Snapshot *aParentSnapshot)
8891{
8892 AssertReturn(!isSnapshotMachine(), E_FAIL);
8893 AssertReturn(!isSessionMachine(), E_FAIL);
8894
8895 HRESULT rc = S_OK;
8896
8897 Utf8Str strStateFile;
8898 if (!data.strStateFile.isEmpty())
8899 {
8900 /* optional */
8901 strStateFile = data.strStateFile;
8902 int vrc = calculateFullPath(strStateFile, strStateFile);
8903 if (RT_FAILURE(vrc))
8904 return setError(E_FAIL,
8905 tr("Invalid saved state file path '%s' (%Rrc)"),
8906 strStateFile.c_str(),
8907 vrc);
8908 }
8909
8910 /* create a snapshot machine object */
8911 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8912 pSnapshotMachine.createObject();
8913 rc = pSnapshotMachine->initFromSettings(this,
8914 data.hardware,
8915 &data.debugging,
8916 &data.autostart,
8917 data.storage,
8918 data.uuid.ref(),
8919 strStateFile);
8920 if (FAILED(rc)) return rc;
8921
8922 /* create a snapshot object */
8923 ComObjPtr<Snapshot> pSnapshot;
8924 pSnapshot.createObject();
8925 /* initialize the snapshot */
8926 rc = pSnapshot->init(mParent, // VirtualBox object
8927 data.uuid,
8928 data.strName,
8929 data.strDescription,
8930 data.timestamp,
8931 pSnapshotMachine,
8932 aParentSnapshot);
8933 if (FAILED(rc)) return rc;
8934
8935 /* memorize the first snapshot if necessary */
8936 if (!mData->mFirstSnapshot)
8937 mData->mFirstSnapshot = pSnapshot;
8938
8939 /* memorize the current snapshot when appropriate */
8940 if ( !mData->mCurrentSnapshot
8941 && pSnapshot->getId() == aCurSnapshotId
8942 )
8943 mData->mCurrentSnapshot = pSnapshot;
8944
8945 // now create the children
8946 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8947 it != data.llChildSnapshots.end();
8948 ++it)
8949 {
8950 const settings::Snapshot &childData = *it;
8951 // recurse
8952 rc = loadSnapshot(childData,
8953 aCurSnapshotId,
8954 pSnapshot); // parent = the one we created above
8955 if (FAILED(rc)) return rc;
8956 }
8957
8958 return rc;
8959}
8960
8961/**
8962 * Loads settings into mHWData.
8963 *
8964 * @param data Reference to the hardware settings.
8965 * @param pDbg Pointer to the debugging settings.
8966 * @param pAutostart Pointer to the autostart settings.
8967 */
8968HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8969 const settings::Autostart *pAutostart)
8970{
8971 AssertReturn(!isSessionMachine(), E_FAIL);
8972
8973 HRESULT rc = S_OK;
8974
8975 try
8976 {
8977 /* The hardware version attribute (optional). */
8978 mHWData->mHWVersion = data.strVersion;
8979 mHWData->mHardwareUUID = data.uuid;
8980
8981 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8982 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8983 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8984 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8985 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8986 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8987 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8988 mHWData->mPAEEnabled = data.fPAE;
8989 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8990 mHWData->mLongMode = data.enmLongMode;
8991 mHWData->mCPUCount = data.cCPUs;
8992 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8993 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8994
8995 // cpu
8996 if (mHWData->mCPUHotPlugEnabled)
8997 {
8998 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8999 it != data.llCpus.end();
9000 ++it)
9001 {
9002 const settings::Cpu &cpu = *it;
9003
9004 mHWData->mCPUAttached[cpu.ulId] = true;
9005 }
9006 }
9007
9008 // cpuid leafs
9009 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9010 it != data.llCpuIdLeafs.end();
9011 ++it)
9012 {
9013 const settings::CpuIdLeaf &leaf = *it;
9014
9015 switch (leaf.ulId)
9016 {
9017 case 0x0:
9018 case 0x1:
9019 case 0x2:
9020 case 0x3:
9021 case 0x4:
9022 case 0x5:
9023 case 0x6:
9024 case 0x7:
9025 case 0x8:
9026 case 0x9:
9027 case 0xA:
9028 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9029 break;
9030
9031 case 0x80000000:
9032 case 0x80000001:
9033 case 0x80000002:
9034 case 0x80000003:
9035 case 0x80000004:
9036 case 0x80000005:
9037 case 0x80000006:
9038 case 0x80000007:
9039 case 0x80000008:
9040 case 0x80000009:
9041 case 0x8000000A:
9042 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9043 break;
9044
9045 default:
9046 /* just ignore */
9047 break;
9048 }
9049 }
9050
9051 mHWData->mMemorySize = data.ulMemorySizeMB;
9052 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9053
9054 // boot order
9055 for (size_t i = 0;
9056 i < RT_ELEMENTS(mHWData->mBootOrder);
9057 i++)
9058 {
9059 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9060 if (it == data.mapBootOrder.end())
9061 mHWData->mBootOrder[i] = DeviceType_Null;
9062 else
9063 mHWData->mBootOrder[i] = it->second;
9064 }
9065
9066 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9067 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9068 mHWData->mMonitorCount = data.cMonitors;
9069 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9070 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9071 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9072 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9073 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9074 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9075 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9076 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9077 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9078 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9079 if (!data.strVideoCaptureFile.isEmpty())
9080 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9081 else
9082 mHWData->mVideoCaptureFile.setNull();
9083 mHWData->mFirmwareType = data.firmwareType;
9084 mHWData->mPointingHIDType = data.pointingHIDType;
9085 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9086 mHWData->mChipsetType = data.chipsetType;
9087 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9088 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9089 mHWData->mHPETEnabled = data.fHPETEnabled;
9090
9091 /* VRDEServer */
9092 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9093 if (FAILED(rc)) return rc;
9094
9095 /* BIOS */
9096 rc = mBIOSSettings->loadSettings(data.biosSettings);
9097 if (FAILED(rc)) return rc;
9098
9099 // Bandwidth control (must come before network adapters)
9100 rc = mBandwidthControl->loadSettings(data.ioSettings);
9101 if (FAILED(rc)) return rc;
9102
9103 /* USB Controller */
9104 rc = mUSBController->loadSettings(data.usbController);
9105 if (FAILED(rc)) return rc;
9106
9107 // network adapters
9108 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9109 uint32_t oldCount = mNetworkAdapters.size();
9110 if (newCount > oldCount)
9111 {
9112 mNetworkAdapters.resize(newCount);
9113 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9114 {
9115 unconst(mNetworkAdapters[slot]).createObject();
9116 mNetworkAdapters[slot]->init(this, slot);
9117 }
9118 }
9119 else if (newCount < oldCount)
9120 mNetworkAdapters.resize(newCount);
9121 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9122 it != data.llNetworkAdapters.end();
9123 ++it)
9124 {
9125 const settings::NetworkAdapter &nic = *it;
9126
9127 /* slot unicity is guaranteed by XML Schema */
9128 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9129 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9130 if (FAILED(rc)) return rc;
9131 }
9132
9133 // serial ports
9134 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9135 it != data.llSerialPorts.end();
9136 ++it)
9137 {
9138 const settings::SerialPort &s = *it;
9139
9140 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9141 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9142 if (FAILED(rc)) return rc;
9143 }
9144
9145 // parallel ports (optional)
9146 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9147 it != data.llParallelPorts.end();
9148 ++it)
9149 {
9150 const settings::ParallelPort &p = *it;
9151
9152 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9153 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9154 if (FAILED(rc)) return rc;
9155 }
9156
9157 /* AudioAdapter */
9158 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9159 if (FAILED(rc)) return rc;
9160
9161 /* Shared folders */
9162 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9163 it != data.llSharedFolders.end();
9164 ++it)
9165 {
9166 const settings::SharedFolder &sf = *it;
9167
9168 ComObjPtr<SharedFolder> sharedFolder;
9169 /* Check for double entries. Not allowed! */
9170 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9171 if (SUCCEEDED(rc))
9172 return setError(VBOX_E_OBJECT_IN_USE,
9173 tr("Shared folder named '%s' already exists"),
9174 sf.strName.c_str());
9175
9176 /* Create the new shared folder. Don't break on error. This will be
9177 * reported when the machine starts. */
9178 sharedFolder.createObject();
9179 rc = sharedFolder->init(getMachine(),
9180 sf.strName,
9181 sf.strHostPath,
9182 RT_BOOL(sf.fWritable),
9183 RT_BOOL(sf.fAutoMount),
9184 false /* fFailOnError */);
9185 if (FAILED(rc)) return rc;
9186 mHWData->mSharedFolders.push_back(sharedFolder);
9187 }
9188
9189 // Clipboard
9190 mHWData->mClipboardMode = data.clipboardMode;
9191
9192 // drag'n'drop
9193 mHWData->mDragAndDropMode = data.dragAndDropMode;
9194
9195 // guest settings
9196 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9197
9198 // IO settings
9199 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9200 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9201
9202 // Host PCI devices
9203 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9204 it != data.pciAttachments.end();
9205 ++it)
9206 {
9207 const settings::HostPCIDeviceAttachment &hpda = *it;
9208 ComObjPtr<PCIDeviceAttachment> pda;
9209
9210 pda.createObject();
9211 pda->loadSettings(this, hpda);
9212 mHWData->mPCIDeviceAssignments.push_back(pda);
9213 }
9214
9215 /*
9216 * (The following isn't really real hardware, but it lives in HWData
9217 * for reasons of convenience.)
9218 */
9219
9220#ifdef VBOX_WITH_GUEST_PROPS
9221 /* Guest properties (optional) */
9222 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9223 it != data.llGuestProperties.end();
9224 ++it)
9225 {
9226 const settings::GuestProperty &prop = *it;
9227 uint32_t fFlags = guestProp::NILFLAG;
9228 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9229 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9230 mHWData->mGuestProperties[prop.strName] = property;
9231 }
9232
9233 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9234#endif /* VBOX_WITH_GUEST_PROPS defined */
9235
9236 rc = loadDebugging(pDbg);
9237 if (FAILED(rc))
9238 return rc;
9239
9240 mHWData->mAutostart = *pAutostart;
9241
9242 /* default frontend */
9243 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9244 }
9245 catch(std::bad_alloc &)
9246 {
9247 return E_OUTOFMEMORY;
9248 }
9249
9250 AssertComRC(rc);
9251 return rc;
9252}
9253
9254/**
9255 * Called from Machine::loadHardware() to load the debugging settings of the
9256 * machine.
9257 *
9258 * @param pDbg Pointer to the settings.
9259 */
9260HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9261{
9262 mHWData->mDebugging = *pDbg;
9263 /* no more processing currently required, this will probably change. */
9264 return S_OK;
9265}
9266
9267/**
9268 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9269 *
9270 * @param data
9271 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9272 * @param puuidSnapshot
9273 * @return
9274 */
9275HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9276 const Guid *puuidRegistry,
9277 const Guid *puuidSnapshot)
9278{
9279 AssertReturn(!isSessionMachine(), E_FAIL);
9280
9281 HRESULT rc = S_OK;
9282
9283 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9284 it != data.llStorageControllers.end();
9285 ++it)
9286 {
9287 const settings::StorageController &ctlData = *it;
9288
9289 ComObjPtr<StorageController> pCtl;
9290 /* Try to find one with the name first. */
9291 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9292 if (SUCCEEDED(rc))
9293 return setError(VBOX_E_OBJECT_IN_USE,
9294 tr("Storage controller named '%s' already exists"),
9295 ctlData.strName.c_str());
9296
9297 pCtl.createObject();
9298 rc = pCtl->init(this,
9299 ctlData.strName,
9300 ctlData.storageBus,
9301 ctlData.ulInstance,
9302 ctlData.fBootable);
9303 if (FAILED(rc)) return rc;
9304
9305 mStorageControllers->push_back(pCtl);
9306
9307 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9308 if (FAILED(rc)) return rc;
9309
9310 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9311 if (FAILED(rc)) return rc;
9312
9313 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9314 if (FAILED(rc)) return rc;
9315
9316 /* Set IDE emulation settings (only for AHCI controller). */
9317 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9318 {
9319 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9320 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9321 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9322 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9323 )
9324 return rc;
9325 }
9326
9327 /* Load the attached devices now. */
9328 rc = loadStorageDevices(pCtl,
9329 ctlData,
9330 puuidRegistry,
9331 puuidSnapshot);
9332 if (FAILED(rc)) return rc;
9333 }
9334
9335 return S_OK;
9336}
9337
9338/**
9339 * Called from loadStorageControllers for a controller's devices.
9340 *
9341 * @param aStorageController
9342 * @param data
9343 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9344 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9345 * @return
9346 */
9347HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9348 const settings::StorageController &data,
9349 const Guid *puuidRegistry,
9350 const Guid *puuidSnapshot)
9351{
9352 HRESULT rc = S_OK;
9353
9354 /* paranoia: detect duplicate attachments */
9355 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9356 it != data.llAttachedDevices.end();
9357 ++it)
9358 {
9359 const settings::AttachedDevice &ad = *it;
9360
9361 for (settings::AttachedDevicesList::const_iterator it2 = it;
9362 it2 != data.llAttachedDevices.end();
9363 ++it2)
9364 {
9365 if (it == it2)
9366 continue;
9367
9368 const settings::AttachedDevice &ad2 = *it2;
9369
9370 if ( ad.lPort == ad2.lPort
9371 && ad.lDevice == ad2.lDevice)
9372 {
9373 return setError(E_FAIL,
9374 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9375 aStorageController->getName().c_str(),
9376 ad.lPort,
9377 ad.lDevice,
9378 mUserData->s.strName.c_str());
9379 }
9380 }
9381 }
9382
9383 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9384 it != data.llAttachedDevices.end();
9385 ++it)
9386 {
9387 const settings::AttachedDevice &dev = *it;
9388 ComObjPtr<Medium> medium;
9389
9390 switch (dev.deviceType)
9391 {
9392 case DeviceType_Floppy:
9393 case DeviceType_DVD:
9394 if (dev.strHostDriveSrc.isNotEmpty())
9395 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9396 else
9397 rc = mParent->findRemoveableMedium(dev.deviceType,
9398 dev.uuid,
9399 false /* fRefresh */,
9400 false /* aSetError */,
9401 medium);
9402 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9403 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9404 rc = S_OK;
9405 break;
9406
9407 case DeviceType_HardDisk:
9408 {
9409 /* find a hard disk by UUID */
9410 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9411 if (FAILED(rc))
9412 {
9413 if (isSnapshotMachine())
9414 {
9415 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9416 // so the user knows that the bad disk is in a snapshot somewhere
9417 com::ErrorInfo info;
9418 return setError(E_FAIL,
9419 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9420 puuidSnapshot->raw(),
9421 info.getText().raw());
9422 }
9423 else
9424 return rc;
9425 }
9426
9427 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9428
9429 if (medium->getType() == MediumType_Immutable)
9430 {
9431 if (isSnapshotMachine())
9432 return setError(E_FAIL,
9433 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9434 "of the virtual machine '%s' ('%s')"),
9435 medium->getLocationFull().c_str(),
9436 dev.uuid.raw(),
9437 puuidSnapshot->raw(),
9438 mUserData->s.strName.c_str(),
9439 mData->m_strConfigFileFull.c_str());
9440
9441 return setError(E_FAIL,
9442 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9443 medium->getLocationFull().c_str(),
9444 dev.uuid.raw(),
9445 mUserData->s.strName.c_str(),
9446 mData->m_strConfigFileFull.c_str());
9447 }
9448
9449 if (medium->getType() == MediumType_MultiAttach)
9450 {
9451 if (isSnapshotMachine())
9452 return setError(E_FAIL,
9453 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9454 "of the virtual machine '%s' ('%s')"),
9455 medium->getLocationFull().c_str(),
9456 dev.uuid.raw(),
9457 puuidSnapshot->raw(),
9458 mUserData->s.strName.c_str(),
9459 mData->m_strConfigFileFull.c_str());
9460
9461 return setError(E_FAIL,
9462 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9463 medium->getLocationFull().c_str(),
9464 dev.uuid.raw(),
9465 mUserData->s.strName.c_str(),
9466 mData->m_strConfigFileFull.c_str());
9467 }
9468
9469 if ( !isSnapshotMachine()
9470 && medium->getChildren().size() != 0
9471 )
9472 return setError(E_FAIL,
9473 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9474 "because it has %d differencing child hard disks"),
9475 medium->getLocationFull().c_str(),
9476 dev.uuid.raw(),
9477 mUserData->s.strName.c_str(),
9478 mData->m_strConfigFileFull.c_str(),
9479 medium->getChildren().size());
9480
9481 if (findAttachment(mMediaData->mAttachments,
9482 medium))
9483 return setError(E_FAIL,
9484 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9485 medium->getLocationFull().c_str(),
9486 dev.uuid.raw(),
9487 mUserData->s.strName.c_str(),
9488 mData->m_strConfigFileFull.c_str());
9489
9490 break;
9491 }
9492
9493 default:
9494 return setError(E_FAIL,
9495 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9496 medium->getLocationFull().c_str(),
9497 mUserData->s.strName.c_str(),
9498 mData->m_strConfigFileFull.c_str());
9499 }
9500
9501 if (FAILED(rc))
9502 break;
9503
9504 /* Bandwidth groups are loaded at this point. */
9505 ComObjPtr<BandwidthGroup> pBwGroup;
9506
9507 if (!dev.strBwGroup.isEmpty())
9508 {
9509 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9510 if (FAILED(rc))
9511 return setError(E_FAIL,
9512 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9513 medium->getLocationFull().c_str(),
9514 dev.strBwGroup.c_str(),
9515 mUserData->s.strName.c_str(),
9516 mData->m_strConfigFileFull.c_str());
9517 pBwGroup->reference();
9518 }
9519
9520 const Bstr controllerName = aStorageController->getName();
9521 ComObjPtr<MediumAttachment> pAttachment;
9522 pAttachment.createObject();
9523 rc = pAttachment->init(this,
9524 medium,
9525 controllerName,
9526 dev.lPort,
9527 dev.lDevice,
9528 dev.deviceType,
9529 false,
9530 dev.fPassThrough,
9531 dev.fTempEject,
9532 dev.fNonRotational,
9533 dev.fDiscard,
9534 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9535 if (FAILED(rc)) break;
9536
9537 /* associate the medium with this machine and snapshot */
9538 if (!medium.isNull())
9539 {
9540 AutoCaller medCaller(medium);
9541 if (FAILED(medCaller.rc())) return medCaller.rc();
9542 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9543
9544 if (isSnapshotMachine())
9545 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9546 else
9547 rc = medium->addBackReference(mData->mUuid);
9548 /* If the medium->addBackReference fails it sets an appropriate
9549 * error message, so no need to do any guesswork here. */
9550
9551 if (puuidRegistry)
9552 // caller wants registry ID to be set on all attached media (OVF import case)
9553 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9554 }
9555
9556 if (FAILED(rc))
9557 break;
9558
9559 /* back up mMediaData to let registeredInit() properly rollback on failure
9560 * (= limited accessibility) */
9561 setModified(IsModified_Storage);
9562 mMediaData.backup();
9563 mMediaData->mAttachments.push_back(pAttachment);
9564 }
9565
9566 return rc;
9567}
9568
9569/**
9570 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9571 *
9572 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9573 * @param aSnapshot where to return the found snapshot
9574 * @param aSetError true to set extended error info on failure
9575 */
9576HRESULT Machine::findSnapshotById(const Guid &aId,
9577 ComObjPtr<Snapshot> &aSnapshot,
9578 bool aSetError /* = false */)
9579{
9580 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9581
9582 if (!mData->mFirstSnapshot)
9583 {
9584 if (aSetError)
9585 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9586 return E_FAIL;
9587 }
9588
9589 if (aId.isZero())
9590 aSnapshot = mData->mFirstSnapshot;
9591 else
9592 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9593
9594 if (!aSnapshot)
9595 {
9596 if (aSetError)
9597 return setError(E_FAIL,
9598 tr("Could not find a snapshot with UUID {%s}"),
9599 aId.toString().c_str());
9600 return E_FAIL;
9601 }
9602
9603 return S_OK;
9604}
9605
9606/**
9607 * Returns the snapshot with the given name or fails of no such snapshot.
9608 *
9609 * @param aName snapshot name to find
9610 * @param aSnapshot where to return the found snapshot
9611 * @param aSetError true to set extended error info on failure
9612 */
9613HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9614 ComObjPtr<Snapshot> &aSnapshot,
9615 bool aSetError /* = false */)
9616{
9617 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9618
9619 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9620
9621 if (!mData->mFirstSnapshot)
9622 {
9623 if (aSetError)
9624 return setError(VBOX_E_OBJECT_NOT_FOUND,
9625 tr("This machine does not have any snapshots"));
9626 return VBOX_E_OBJECT_NOT_FOUND;
9627 }
9628
9629 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9630
9631 if (!aSnapshot)
9632 {
9633 if (aSetError)
9634 return setError(VBOX_E_OBJECT_NOT_FOUND,
9635 tr("Could not find a snapshot named '%s'"), strName.c_str());
9636 return VBOX_E_OBJECT_NOT_FOUND;
9637 }
9638
9639 return S_OK;
9640}
9641
9642/**
9643 * Returns a storage controller object with the given name.
9644 *
9645 * @param aName storage controller name to find
9646 * @param aStorageController where to return the found storage controller
9647 * @param aSetError true to set extended error info on failure
9648 */
9649HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9650 ComObjPtr<StorageController> &aStorageController,
9651 bool aSetError /* = false */)
9652{
9653 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9654
9655 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9656 it != mStorageControllers->end();
9657 ++it)
9658 {
9659 if ((*it)->getName() == aName)
9660 {
9661 aStorageController = (*it);
9662 return S_OK;
9663 }
9664 }
9665
9666 if (aSetError)
9667 return setError(VBOX_E_OBJECT_NOT_FOUND,
9668 tr("Could not find a storage controller named '%s'"),
9669 aName.c_str());
9670 return VBOX_E_OBJECT_NOT_FOUND;
9671}
9672
9673HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9674 MediaData::AttachmentList &atts)
9675{
9676 AutoCaller autoCaller(this);
9677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9678
9679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9680
9681 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9682 it != mMediaData->mAttachments.end();
9683 ++it)
9684 {
9685 const ComObjPtr<MediumAttachment> &pAtt = *it;
9686
9687 // should never happen, but deal with NULL pointers in the list.
9688 AssertStmt(!pAtt.isNull(), continue);
9689
9690 // getControllerName() needs caller+read lock
9691 AutoCaller autoAttCaller(pAtt);
9692 if (FAILED(autoAttCaller.rc()))
9693 {
9694 atts.clear();
9695 return autoAttCaller.rc();
9696 }
9697 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9698
9699 if (pAtt->getControllerName() == aName)
9700 atts.push_back(pAtt);
9701 }
9702
9703 return S_OK;
9704}
9705
9706/**
9707 * Helper for #saveSettings. Cares about renaming the settings directory and
9708 * file if the machine name was changed and about creating a new settings file
9709 * if this is a new machine.
9710 *
9711 * @note Must be never called directly but only from #saveSettings().
9712 */
9713HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9714{
9715 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9716
9717 HRESULT rc = S_OK;
9718
9719 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9720
9721 /// @todo need to handle primary group change, too
9722
9723 /* attempt to rename the settings file if machine name is changed */
9724 if ( mUserData->s.fNameSync
9725 && mUserData.isBackedUp()
9726 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9727 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9728 )
9729 {
9730 bool dirRenamed = false;
9731 bool fileRenamed = false;
9732
9733 Utf8Str configFile, newConfigFile;
9734 Utf8Str configFilePrev, newConfigFilePrev;
9735 Utf8Str configDir, newConfigDir;
9736
9737 do
9738 {
9739 int vrc = VINF_SUCCESS;
9740
9741 Utf8Str name = mUserData.backedUpData()->s.strName;
9742 Utf8Str newName = mUserData->s.strName;
9743 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9744 if (group == "/")
9745 group.setNull();
9746 Utf8Str newGroup = mUserData->s.llGroups.front();
9747 if (newGroup == "/")
9748 newGroup.setNull();
9749
9750 configFile = mData->m_strConfigFileFull;
9751
9752 /* first, rename the directory if it matches the group and machine name */
9753 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9754 group.c_str(), RTPATH_DELIMITER, name.c_str());
9755 /** @todo hack, make somehow use of ComposeMachineFilename */
9756 if (mUserData->s.fDirectoryIncludesUUID)
9757 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9758 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9759 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9760 /** @todo hack, make somehow use of ComposeMachineFilename */
9761 if (mUserData->s.fDirectoryIncludesUUID)
9762 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9763 configDir = configFile;
9764 configDir.stripFilename();
9765 newConfigDir = configDir;
9766 if ( configDir.length() >= groupPlusName.length()
9767 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9768 {
9769 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9770 Utf8Str newConfigBaseDir(newConfigDir);
9771 newConfigDir.append(newGroupPlusName);
9772 /* consistency: use \ if appropriate on the platform */
9773 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9774 /* new dir and old dir cannot be equal here because of 'if'
9775 * above and because name != newName */
9776 Assert(configDir != newConfigDir);
9777 if (!fSettingsFileIsNew)
9778 {
9779 /* perform real rename only if the machine is not new */
9780 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9781 if ( vrc == VERR_FILE_NOT_FOUND
9782 || vrc == VERR_PATH_NOT_FOUND)
9783 {
9784 /* create the parent directory, then retry renaming */
9785 Utf8Str parent(newConfigDir);
9786 parent.stripFilename();
9787 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9788 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9789 }
9790 if (RT_FAILURE(vrc))
9791 {
9792 rc = setError(E_FAIL,
9793 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9794 configDir.c_str(),
9795 newConfigDir.c_str(),
9796 vrc);
9797 break;
9798 }
9799 /* delete subdirectories which are no longer needed */
9800 Utf8Str dir(configDir);
9801 dir.stripFilename();
9802 while (dir != newConfigBaseDir && dir != ".")
9803 {
9804 vrc = RTDirRemove(dir.c_str());
9805 if (RT_FAILURE(vrc))
9806 break;
9807 dir.stripFilename();
9808 }
9809 dirRenamed = true;
9810 }
9811 }
9812
9813 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9814 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9815
9816 /* then try to rename the settings file itself */
9817 if (newConfigFile != configFile)
9818 {
9819 /* get the path to old settings file in renamed directory */
9820 configFile = Utf8StrFmt("%s%c%s",
9821 newConfigDir.c_str(),
9822 RTPATH_DELIMITER,
9823 RTPathFilename(configFile.c_str()));
9824 if (!fSettingsFileIsNew)
9825 {
9826 /* perform real rename only if the machine is not new */
9827 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9828 if (RT_FAILURE(vrc))
9829 {
9830 rc = setError(E_FAIL,
9831 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9832 configFile.c_str(),
9833 newConfigFile.c_str(),
9834 vrc);
9835 break;
9836 }
9837 fileRenamed = true;
9838 configFilePrev = configFile;
9839 configFilePrev += "-prev";
9840 newConfigFilePrev = newConfigFile;
9841 newConfigFilePrev += "-prev";
9842 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9843 }
9844 }
9845
9846 // update m_strConfigFileFull amd mConfigFile
9847 mData->m_strConfigFileFull = newConfigFile;
9848 // compute the relative path too
9849 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9850
9851 // store the old and new so that VirtualBox::saveSettings() can update
9852 // the media registry
9853 if ( mData->mRegistered
9854 && configDir != newConfigDir)
9855 {
9856 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9857
9858 if (pfNeedsGlobalSaveSettings)
9859 *pfNeedsGlobalSaveSettings = true;
9860 }
9861
9862 // in the saved state file path, replace the old directory with the new directory
9863 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9864 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9865
9866 // and do the same thing for the saved state file paths of all the online snapshots
9867 if (mData->mFirstSnapshot)
9868 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9869 newConfigDir.c_str());
9870 }
9871 while (0);
9872
9873 if (FAILED(rc))
9874 {
9875 /* silently try to rename everything back */
9876 if (fileRenamed)
9877 {
9878 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9879 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9880 }
9881 if (dirRenamed)
9882 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9883 }
9884
9885 if (FAILED(rc)) return rc;
9886 }
9887
9888 if (fSettingsFileIsNew)
9889 {
9890 /* create a virgin config file */
9891 int vrc = VINF_SUCCESS;
9892
9893 /* ensure the settings directory exists */
9894 Utf8Str path(mData->m_strConfigFileFull);
9895 path.stripFilename();
9896 if (!RTDirExists(path.c_str()))
9897 {
9898 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9899 if (RT_FAILURE(vrc))
9900 {
9901 return setError(E_FAIL,
9902 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9903 path.c_str(),
9904 vrc);
9905 }
9906 }
9907
9908 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9909 path = Utf8Str(mData->m_strConfigFileFull);
9910 RTFILE f = NIL_RTFILE;
9911 vrc = RTFileOpen(&f, path.c_str(),
9912 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9913 if (RT_FAILURE(vrc))
9914 return setError(E_FAIL,
9915 tr("Could not create the settings file '%s' (%Rrc)"),
9916 path.c_str(),
9917 vrc);
9918 RTFileClose(f);
9919 }
9920
9921 return rc;
9922}
9923
9924/**
9925 * Saves and commits machine data, user data and hardware data.
9926 *
9927 * Note that on failure, the data remains uncommitted.
9928 *
9929 * @a aFlags may combine the following flags:
9930 *
9931 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9932 * Used when saving settings after an operation that makes them 100%
9933 * correspond to the settings from the current snapshot.
9934 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9935 * #isReallyModified() returns false. This is necessary for cases when we
9936 * change machine data directly, not through the backup()/commit() mechanism.
9937 * - SaveS_Force: settings will be saved without doing a deep compare of the
9938 * settings structures. This is used when this is called because snapshots
9939 * have changed to avoid the overhead of the deep compare.
9940 *
9941 * @note Must be called from under this object's write lock. Locks children for
9942 * writing.
9943 *
9944 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9945 * initialized to false and that will be set to true by this function if
9946 * the caller must invoke VirtualBox::saveSettings() because the global
9947 * settings have changed. This will happen if a machine rename has been
9948 * saved and the global machine and media registries will therefore need
9949 * updating.
9950 */
9951HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9952 int aFlags /*= 0*/)
9953{
9954 LogFlowThisFuncEnter();
9955
9956 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9957
9958 /* make sure child objects are unable to modify the settings while we are
9959 * saving them */
9960 ensureNoStateDependencies();
9961
9962 AssertReturn(!isSnapshotMachine(),
9963 E_FAIL);
9964
9965 HRESULT rc = S_OK;
9966 bool fNeedsWrite = false;
9967
9968 /* First, prepare to save settings. It will care about renaming the
9969 * settings directory and file if the machine name was changed and about
9970 * creating a new settings file if this is a new machine. */
9971 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9972 if (FAILED(rc)) return rc;
9973
9974 // keep a pointer to the current settings structures
9975 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9976 settings::MachineConfigFile *pNewConfig = NULL;
9977
9978 try
9979 {
9980 // make a fresh one to have everyone write stuff into
9981 pNewConfig = new settings::MachineConfigFile(NULL);
9982 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9983
9984 // now go and copy all the settings data from COM to the settings structures
9985 // (this calles saveSettings() on all the COM objects in the machine)
9986 copyMachineDataToSettings(*pNewConfig);
9987
9988 if (aFlags & SaveS_ResetCurStateModified)
9989 {
9990 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9991 mData->mCurrentStateModified = FALSE;
9992 fNeedsWrite = true; // always, no need to compare
9993 }
9994 else if (aFlags & SaveS_Force)
9995 {
9996 fNeedsWrite = true; // always, no need to compare
9997 }
9998 else
9999 {
10000 if (!mData->mCurrentStateModified)
10001 {
10002 // do a deep compare of the settings that we just saved with the settings
10003 // previously stored in the config file; this invokes MachineConfigFile::operator==
10004 // which does a deep compare of all the settings, which is expensive but less expensive
10005 // than writing out XML in vain
10006 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10007
10008 // could still be modified if any settings changed
10009 mData->mCurrentStateModified = fAnySettingsChanged;
10010
10011 fNeedsWrite = fAnySettingsChanged;
10012 }
10013 else
10014 fNeedsWrite = true;
10015 }
10016
10017 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10018
10019 if (fNeedsWrite)
10020 // now spit it all out!
10021 pNewConfig->write(mData->m_strConfigFileFull);
10022
10023 mData->pMachineConfigFile = pNewConfig;
10024 delete pOldConfig;
10025 commit();
10026
10027 // after saving settings, we are no longer different from the XML on disk
10028 mData->flModifications = 0;
10029 }
10030 catch (HRESULT err)
10031 {
10032 // we assume that error info is set by the thrower
10033 rc = err;
10034
10035 // restore old config
10036 delete pNewConfig;
10037 mData->pMachineConfigFile = pOldConfig;
10038 }
10039 catch (...)
10040 {
10041 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10042 }
10043
10044 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10045 {
10046 /* Fire the data change event, even on failure (since we've already
10047 * committed all data). This is done only for SessionMachines because
10048 * mutable Machine instances are always not registered (i.e. private
10049 * to the client process that creates them) and thus don't need to
10050 * inform callbacks. */
10051 if (isSessionMachine())
10052 mParent->onMachineDataChange(mData->mUuid);
10053 }
10054
10055 LogFlowThisFunc(("rc=%08X\n", rc));
10056 LogFlowThisFuncLeave();
10057 return rc;
10058}
10059
10060/**
10061 * Implementation for saving the machine settings into the given
10062 * settings::MachineConfigFile instance. This copies machine extradata
10063 * from the previous machine config file in the instance data, if any.
10064 *
10065 * This gets called from two locations:
10066 *
10067 * -- Machine::saveSettings(), during the regular XML writing;
10068 *
10069 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10070 * exported to OVF and we write the VirtualBox proprietary XML
10071 * into a <vbox:Machine> tag.
10072 *
10073 * This routine fills all the fields in there, including snapshots, *except*
10074 * for the following:
10075 *
10076 * -- fCurrentStateModified. There is some special logic associated with that.
10077 *
10078 * The caller can then call MachineConfigFile::write() or do something else
10079 * with it.
10080 *
10081 * Caller must hold the machine lock!
10082 *
10083 * This throws XML errors and HRESULT, so the caller must have a catch block!
10084 */
10085void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10086{
10087 // deep copy extradata
10088 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10089
10090 config.uuid = mData->mUuid;
10091
10092 // copy name, description, OS type, teleport, UTC etc.
10093 config.machineUserData = mUserData->s;
10094
10095 // Encode the Icon Override data from Machine and store on config userdata.
10096 com::SafeArray<BYTE> iconByte;
10097 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10098 ssize_t cbData = iconByte.size();
10099 if (cbData > 0)
10100 {
10101 ssize_t cchOut = RTBase64EncodedLength(cbData);
10102 Utf8Str strIconData;
10103 strIconData.reserve(cchOut+1);
10104 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10105 strIconData.mutableRaw(), strIconData.capacity(),
10106 NULL);
10107 if (RT_FAILURE(vrc))
10108 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10109 strIconData.jolt();
10110 config.machineUserData.ovIcon = strIconData;
10111 }
10112 else
10113 config.machineUserData.ovIcon.setNull();
10114
10115 if ( mData->mMachineState == MachineState_Saved
10116 || mData->mMachineState == MachineState_Restoring
10117 // when deleting a snapshot we may or may not have a saved state in the current state,
10118 // so let's not assert here please
10119 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10120 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10121 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10122 && (!mSSData->strStateFilePath.isEmpty())
10123 )
10124 )
10125 {
10126 Assert(!mSSData->strStateFilePath.isEmpty());
10127 /* try to make the file name relative to the settings file dir */
10128 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10129 }
10130 else
10131 {
10132 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10133 config.strStateFile.setNull();
10134 }
10135
10136 if (mData->mCurrentSnapshot)
10137 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10138 else
10139 config.uuidCurrentSnapshot.clear();
10140
10141 config.timeLastStateChange = mData->mLastStateChange;
10142 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10143 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10144
10145 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10146 if (FAILED(rc)) throw rc;
10147
10148 rc = saveStorageControllers(config.storageMachine);
10149 if (FAILED(rc)) throw rc;
10150
10151 // save machine's media registry if this is VirtualBox 4.0 or later
10152 if (config.canHaveOwnMediaRegistry())
10153 {
10154 // determine machine folder
10155 Utf8Str strMachineFolder = getSettingsFileFull();
10156 strMachineFolder.stripFilename();
10157 mParent->saveMediaRegistry(config.mediaRegistry,
10158 getId(), // only media with registry ID == machine UUID
10159 strMachineFolder);
10160 // this throws HRESULT
10161 }
10162
10163 // save snapshots
10164 rc = saveAllSnapshots(config);
10165 if (FAILED(rc)) throw rc;
10166}
10167
10168/**
10169 * Saves all snapshots of the machine into the given machine config file. Called
10170 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10171 * @param config
10172 * @return
10173 */
10174HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10175{
10176 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10177
10178 HRESULT rc = S_OK;
10179
10180 try
10181 {
10182 config.llFirstSnapshot.clear();
10183
10184 if (mData->mFirstSnapshot)
10185 {
10186 settings::Snapshot snapNew;
10187 config.llFirstSnapshot.push_back(snapNew);
10188
10189 // get reference to the fresh copy of the snapshot on the list and
10190 // work on that copy directly to avoid excessive copying later
10191 settings::Snapshot &snap = config.llFirstSnapshot.front();
10192
10193 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10194 if (FAILED(rc)) throw rc;
10195 }
10196
10197// if (mType == IsSessionMachine)
10198// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10199
10200 }
10201 catch (HRESULT err)
10202 {
10203 /* we assume that error info is set by the thrower */
10204 rc = err;
10205 }
10206 catch (...)
10207 {
10208 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10209 }
10210
10211 return rc;
10212}
10213
10214/**
10215 * Saves the VM hardware configuration. It is assumed that the
10216 * given node is empty.
10217 *
10218 * @param data Reference to the settings object for the hardware config.
10219 * @param pDbg Pointer to the settings object for the debugging config
10220 * which happens to live in mHWData.
10221 * @param pAutostart Pointer to the settings object for the autostart config
10222 * which happens to live in mHWData.
10223 */
10224HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10225 settings::Autostart *pAutostart)
10226{
10227 HRESULT rc = S_OK;
10228
10229 try
10230 {
10231 /* The hardware version attribute (optional).
10232 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10233 if ( mHWData->mHWVersion == "1"
10234 && mSSData->strStateFilePath.isEmpty()
10235 )
10236 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. */
10237
10238 data.strVersion = mHWData->mHWVersion;
10239 data.uuid = mHWData->mHardwareUUID;
10240
10241 // CPU
10242 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10243 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10244 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10245 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10246 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10247 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10248 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10249 data.fPAE = !!mHWData->mPAEEnabled;
10250 data.enmLongMode = mHWData->mLongMode;
10251 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10252
10253 /* Standard and Extended CPUID leafs. */
10254 data.llCpuIdLeafs.clear();
10255 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10256 {
10257 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10258 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10259 }
10260 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10261 {
10262 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10263 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10264 }
10265
10266 data.cCPUs = mHWData->mCPUCount;
10267 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10268 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10269
10270 data.llCpus.clear();
10271 if (data.fCpuHotPlug)
10272 {
10273 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10274 {
10275 if (mHWData->mCPUAttached[idx])
10276 {
10277 settings::Cpu cpu;
10278 cpu.ulId = idx;
10279 data.llCpus.push_back(cpu);
10280 }
10281 }
10282 }
10283
10284 // memory
10285 data.ulMemorySizeMB = mHWData->mMemorySize;
10286 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10287
10288 // firmware
10289 data.firmwareType = mHWData->mFirmwareType;
10290
10291 // HID
10292 data.pointingHIDType = mHWData->mPointingHIDType;
10293 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10294
10295 // chipset
10296 data.chipsetType = mHWData->mChipsetType;
10297
10298 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10299 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10300
10301 // HPET
10302 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10303
10304 // boot order
10305 data.mapBootOrder.clear();
10306 for (size_t i = 0;
10307 i < RT_ELEMENTS(mHWData->mBootOrder);
10308 ++i)
10309 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10310
10311 // display
10312 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10313 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10314 data.cMonitors = mHWData->mMonitorCount;
10315 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10316 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10317 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10318 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10319 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10320 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10321 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10322 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10323 {
10324 if (mHWData->maVideoCaptureScreens[i])
10325 ASMBitSet(&data.u64VideoCaptureScreens, i);
10326 else
10327 ASMBitClear(&data.u64VideoCaptureScreens, i);
10328 }
10329 /* store relative video capture file if possible */
10330 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10331
10332 /* VRDEServer settings (optional) */
10333 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10334 if (FAILED(rc)) throw rc;
10335
10336 /* BIOS (required) */
10337 rc = mBIOSSettings->saveSettings(data.biosSettings);
10338 if (FAILED(rc)) throw rc;
10339
10340 /* USB Controller (required) */
10341 rc = mUSBController->saveSettings(data.usbController);
10342 if (FAILED(rc)) throw rc;
10343
10344 /* Network adapters (required) */
10345 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10346 data.llNetworkAdapters.clear();
10347 /* Write out only the nominal number of network adapters for this
10348 * chipset type. Since Machine::commit() hasn't been called there
10349 * may be extra NIC settings in the vector. */
10350 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10351 {
10352 settings::NetworkAdapter nic;
10353 nic.ulSlot = slot;
10354 /* paranoia check... must not be NULL, but must not crash either. */
10355 if (mNetworkAdapters[slot])
10356 {
10357 rc = mNetworkAdapters[slot]->saveSettings(nic);
10358 if (FAILED(rc)) throw rc;
10359
10360 data.llNetworkAdapters.push_back(nic);
10361 }
10362 }
10363
10364 /* Serial ports */
10365 data.llSerialPorts.clear();
10366 for (ULONG slot = 0;
10367 slot < RT_ELEMENTS(mSerialPorts);
10368 ++slot)
10369 {
10370 settings::SerialPort s;
10371 s.ulSlot = slot;
10372 rc = mSerialPorts[slot]->saveSettings(s);
10373 if (FAILED(rc)) return rc;
10374
10375 data.llSerialPorts.push_back(s);
10376 }
10377
10378 /* Parallel ports */
10379 data.llParallelPorts.clear();
10380 for (ULONG slot = 0;
10381 slot < RT_ELEMENTS(mParallelPorts);
10382 ++slot)
10383 {
10384 settings::ParallelPort p;
10385 p.ulSlot = slot;
10386 rc = mParallelPorts[slot]->saveSettings(p);
10387 if (FAILED(rc)) return rc;
10388
10389 data.llParallelPorts.push_back(p);
10390 }
10391
10392 /* Audio adapter */
10393 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10394 if (FAILED(rc)) return rc;
10395
10396 /* Shared folders */
10397 data.llSharedFolders.clear();
10398 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10399 it != mHWData->mSharedFolders.end();
10400 ++it)
10401 {
10402 SharedFolder *pSF = *it;
10403 AutoCaller sfCaller(pSF);
10404 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10405 settings::SharedFolder sf;
10406 sf.strName = pSF->getName();
10407 sf.strHostPath = pSF->getHostPath();
10408 sf.fWritable = !!pSF->isWritable();
10409 sf.fAutoMount = !!pSF->isAutoMounted();
10410
10411 data.llSharedFolders.push_back(sf);
10412 }
10413
10414 // clipboard
10415 data.clipboardMode = mHWData->mClipboardMode;
10416
10417 // drag'n'drop
10418 data.dragAndDropMode = mHWData->mDragAndDropMode;
10419
10420 /* Guest */
10421 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10422
10423 // IO settings
10424 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10425 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10426
10427 /* BandwidthControl (required) */
10428 rc = mBandwidthControl->saveSettings(data.ioSettings);
10429 if (FAILED(rc)) throw rc;
10430
10431 /* Host PCI devices */
10432 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10433 it != mHWData->mPCIDeviceAssignments.end();
10434 ++it)
10435 {
10436 ComObjPtr<PCIDeviceAttachment> pda = *it;
10437 settings::HostPCIDeviceAttachment hpda;
10438
10439 rc = pda->saveSettings(hpda);
10440 if (FAILED(rc)) throw rc;
10441
10442 data.pciAttachments.push_back(hpda);
10443 }
10444
10445
10446 // guest properties
10447 data.llGuestProperties.clear();
10448#ifdef VBOX_WITH_GUEST_PROPS
10449 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10450 it != mHWData->mGuestProperties.end();
10451 ++it)
10452 {
10453 HWData::GuestProperty property = it->second;
10454
10455 /* Remove transient guest properties at shutdown unless we
10456 * are saving state */
10457 if ( ( mData->mMachineState == MachineState_PoweredOff
10458 || mData->mMachineState == MachineState_Aborted
10459 || mData->mMachineState == MachineState_Teleported)
10460 && ( property.mFlags & guestProp::TRANSIENT
10461 || property.mFlags & guestProp::TRANSRESET))
10462 continue;
10463 settings::GuestProperty prop;
10464 prop.strName = it->first;
10465 prop.strValue = property.strValue;
10466 prop.timestamp = property.mTimestamp;
10467 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10468 guestProp::writeFlags(property.mFlags, szFlags);
10469 prop.strFlags = szFlags;
10470
10471 data.llGuestProperties.push_back(prop);
10472 }
10473
10474 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10475 /* I presume this doesn't require a backup(). */
10476 mData->mGuestPropertiesModified = FALSE;
10477#endif /* VBOX_WITH_GUEST_PROPS defined */
10478
10479 *pDbg = mHWData->mDebugging;
10480 *pAutostart = mHWData->mAutostart;
10481
10482 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10483 }
10484 catch(std::bad_alloc &)
10485 {
10486 return E_OUTOFMEMORY;
10487 }
10488
10489 AssertComRC(rc);
10490 return rc;
10491}
10492
10493/**
10494 * Saves the storage controller configuration.
10495 *
10496 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10497 */
10498HRESULT Machine::saveStorageControllers(settings::Storage &data)
10499{
10500 data.llStorageControllers.clear();
10501
10502 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10503 it != mStorageControllers->end();
10504 ++it)
10505 {
10506 HRESULT rc;
10507 ComObjPtr<StorageController> pCtl = *it;
10508
10509 settings::StorageController ctl;
10510 ctl.strName = pCtl->getName();
10511 ctl.controllerType = pCtl->getControllerType();
10512 ctl.storageBus = pCtl->getStorageBus();
10513 ctl.ulInstance = pCtl->getInstance();
10514 ctl.fBootable = pCtl->getBootable();
10515
10516 /* Save the port count. */
10517 ULONG portCount;
10518 rc = pCtl->COMGETTER(PortCount)(&portCount);
10519 ComAssertComRCRet(rc, rc);
10520 ctl.ulPortCount = portCount;
10521
10522 /* Save fUseHostIOCache */
10523 BOOL fUseHostIOCache;
10524 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10525 ComAssertComRCRet(rc, rc);
10526 ctl.fUseHostIOCache = !!fUseHostIOCache;
10527
10528 /* Save IDE emulation settings. */
10529 if (ctl.controllerType == StorageControllerType_IntelAhci)
10530 {
10531 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10532 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10533 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10534 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10535 )
10536 ComAssertComRCRet(rc, rc);
10537 }
10538
10539 /* save the devices now. */
10540 rc = saveStorageDevices(pCtl, ctl);
10541 ComAssertComRCRet(rc, rc);
10542
10543 data.llStorageControllers.push_back(ctl);
10544 }
10545
10546 return S_OK;
10547}
10548
10549/**
10550 * Saves the hard disk configuration.
10551 */
10552HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10553 settings::StorageController &data)
10554{
10555 MediaData::AttachmentList atts;
10556
10557 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10558 if (FAILED(rc)) return rc;
10559
10560 data.llAttachedDevices.clear();
10561 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10562 it != atts.end();
10563 ++it)
10564 {
10565 settings::AttachedDevice dev;
10566
10567 MediumAttachment *pAttach = *it;
10568 Medium *pMedium = pAttach->getMedium();
10569
10570 dev.deviceType = pAttach->getType();
10571 dev.lPort = pAttach->getPort();
10572 dev.lDevice = pAttach->getDevice();
10573 if (pMedium)
10574 {
10575 if (pMedium->isHostDrive())
10576 dev.strHostDriveSrc = pMedium->getLocationFull();
10577 else
10578 dev.uuid = pMedium->getId();
10579 dev.fPassThrough = pAttach->getPassthrough();
10580 dev.fTempEject = pAttach->getTempEject();
10581 dev.fNonRotational = pAttach->getNonRotational();
10582 dev.fDiscard = pAttach->getDiscard();
10583 }
10584
10585 dev.strBwGroup = pAttach->getBandwidthGroup();
10586
10587 data.llAttachedDevices.push_back(dev);
10588 }
10589
10590 return S_OK;
10591}
10592
10593/**
10594 * Saves machine state settings as defined by aFlags
10595 * (SaveSTS_* values).
10596 *
10597 * @param aFlags Combination of SaveSTS_* flags.
10598 *
10599 * @note Locks objects for writing.
10600 */
10601HRESULT Machine::saveStateSettings(int aFlags)
10602{
10603 if (aFlags == 0)
10604 return S_OK;
10605
10606 AutoCaller autoCaller(this);
10607 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10608
10609 /* This object's write lock is also necessary to serialize file access
10610 * (prevent concurrent reads and writes) */
10611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10612
10613 HRESULT rc = S_OK;
10614
10615 Assert(mData->pMachineConfigFile);
10616
10617 try
10618 {
10619 if (aFlags & SaveSTS_CurStateModified)
10620 mData->pMachineConfigFile->fCurrentStateModified = true;
10621
10622 if (aFlags & SaveSTS_StateFilePath)
10623 {
10624 if (!mSSData->strStateFilePath.isEmpty())
10625 /* try to make the file name relative to the settings file dir */
10626 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10627 else
10628 mData->pMachineConfigFile->strStateFile.setNull();
10629 }
10630
10631 if (aFlags & SaveSTS_StateTimeStamp)
10632 {
10633 Assert( mData->mMachineState != MachineState_Aborted
10634 || mSSData->strStateFilePath.isEmpty());
10635
10636 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10637
10638 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10639//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10640 }
10641
10642 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10643 }
10644 catch (...)
10645 {
10646 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10647 }
10648
10649 return rc;
10650}
10651
10652/**
10653 * Ensures that the given medium is added to a media registry. If this machine
10654 * was created with 4.0 or later, then the machine registry is used. Otherwise
10655 * the global VirtualBox media registry is used.
10656 *
10657 * Caller must NOT hold machine lock, media tree or any medium locks!
10658 *
10659 * @param pMedium
10660 */
10661void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10662{
10663 /* Paranoia checks: do not hold machine or media tree locks. */
10664 AssertReturnVoid(!isWriteLockOnCurrentThread());
10665 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10666
10667 ComObjPtr<Medium> pBase;
10668 {
10669 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10670 pBase = pMedium->getBase();
10671 }
10672
10673 /* Paranoia checks: do not hold medium locks. */
10674 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10675 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10676
10677 // decide which medium registry to use now that the medium is attached:
10678 Guid uuid;
10679 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10680 // machine XML is VirtualBox 4.0 or higher:
10681 uuid = getId(); // machine UUID
10682 else
10683 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10684
10685 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10686 mParent->markRegistryModified(uuid);
10687
10688 /* For more complex hard disk structures it can happen that the base
10689 * medium isn't yet associated with any medium registry. Do that now. */
10690 if (pMedium != pBase)
10691 {
10692 if (pBase->addRegistry(uuid, true /* fRecurse */))
10693 mParent->markRegistryModified(uuid);
10694 }
10695}
10696
10697/**
10698 * Creates differencing hard disks for all normal hard disks attached to this
10699 * machine and a new set of attachments to refer to created disks.
10700 *
10701 * Used when taking a snapshot or when deleting the current state. Gets called
10702 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10703 *
10704 * This method assumes that mMediaData contains the original hard disk attachments
10705 * it needs to create diffs for. On success, these attachments will be replaced
10706 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10707 * called to delete created diffs which will also rollback mMediaData and restore
10708 * whatever was backed up before calling this method.
10709 *
10710 * Attachments with non-normal hard disks are left as is.
10711 *
10712 * If @a aOnline is @c false then the original hard disks that require implicit
10713 * diffs will be locked for reading. Otherwise it is assumed that they are
10714 * already locked for writing (when the VM was started). Note that in the latter
10715 * case it is responsibility of the caller to lock the newly created diffs for
10716 * writing if this method succeeds.
10717 *
10718 * @param aProgress Progress object to run (must contain at least as
10719 * many operations left as the number of hard disks
10720 * attached).
10721 * @param aOnline Whether the VM was online prior to this operation.
10722 *
10723 * @note The progress object is not marked as completed, neither on success nor
10724 * on failure. This is a responsibility of the caller.
10725 *
10726 * @note Locks this object and the media tree for writing.
10727 */
10728HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10729 ULONG aWeight,
10730 bool aOnline)
10731{
10732 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10733
10734 AutoCaller autoCaller(this);
10735 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10736
10737 AutoMultiWriteLock2 alock(this->lockHandle(),
10738 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10739
10740 /* must be in a protective state because we release the lock below */
10741 AssertReturn( mData->mMachineState == MachineState_Saving
10742 || mData->mMachineState == MachineState_LiveSnapshotting
10743 || mData->mMachineState == MachineState_RestoringSnapshot
10744 || mData->mMachineState == MachineState_DeletingSnapshot
10745 , E_FAIL);
10746
10747 HRESULT rc = S_OK;
10748
10749 // use appropriate locked media map (online or offline)
10750 MediumLockListMap lockedMediaOffline;
10751 MediumLockListMap *lockedMediaMap;
10752 if (aOnline)
10753 lockedMediaMap = &mData->mSession.mLockedMedia;
10754 else
10755 lockedMediaMap = &lockedMediaOffline;
10756
10757 try
10758 {
10759 if (!aOnline)
10760 {
10761 /* lock all attached hard disks early to detect "in use"
10762 * situations before creating actual diffs */
10763 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10764 it != mMediaData->mAttachments.end();
10765 ++it)
10766 {
10767 MediumAttachment* pAtt = *it;
10768 if (pAtt->getType() == DeviceType_HardDisk)
10769 {
10770 Medium* pMedium = pAtt->getMedium();
10771 Assert(pMedium);
10772
10773 MediumLockList *pMediumLockList(new MediumLockList());
10774 alock.release();
10775 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10776 false /* fMediumLockWrite */,
10777 NULL,
10778 *pMediumLockList);
10779 alock.acquire();
10780 if (FAILED(rc))
10781 {
10782 delete pMediumLockList;
10783 throw rc;
10784 }
10785 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10786 if (FAILED(rc))
10787 {
10788 throw setError(rc,
10789 tr("Collecting locking information for all attached media failed"));
10790 }
10791 }
10792 }
10793
10794 /* Now lock all media. If this fails, nothing is locked. */
10795 alock.release();
10796 rc = lockedMediaMap->Lock();
10797 alock.acquire();
10798 if (FAILED(rc))
10799 {
10800 throw setError(rc,
10801 tr("Locking of attached media failed"));
10802 }
10803 }
10804
10805 /* remember the current list (note that we don't use backup() since
10806 * mMediaData may be already backed up) */
10807 MediaData::AttachmentList atts = mMediaData->mAttachments;
10808
10809 /* start from scratch */
10810 mMediaData->mAttachments.clear();
10811
10812 /* go through remembered attachments and create diffs for normal hard
10813 * disks and attach them */
10814 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10815 it != atts.end();
10816 ++it)
10817 {
10818 MediumAttachment* pAtt = *it;
10819
10820 DeviceType_T devType = pAtt->getType();
10821 Medium* pMedium = pAtt->getMedium();
10822
10823 if ( devType != DeviceType_HardDisk
10824 || pMedium == NULL
10825 || pMedium->getType() != MediumType_Normal)
10826 {
10827 /* copy the attachment as is */
10828
10829 /** @todo the progress object created in Console::TakeSnaphot
10830 * only expects operations for hard disks. Later other
10831 * device types need to show up in the progress as well. */
10832 if (devType == DeviceType_HardDisk)
10833 {
10834 if (pMedium == NULL)
10835 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10836 aWeight); // weight
10837 else
10838 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10839 pMedium->getBase()->getName().c_str()).raw(),
10840 aWeight); // weight
10841 }
10842
10843 mMediaData->mAttachments.push_back(pAtt);
10844 continue;
10845 }
10846
10847 /* need a diff */
10848 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10849 pMedium->getBase()->getName().c_str()).raw(),
10850 aWeight); // weight
10851
10852 Utf8Str strFullSnapshotFolder;
10853 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10854
10855 ComObjPtr<Medium> diff;
10856 diff.createObject();
10857 // store the diff in the same registry as the parent
10858 // (this cannot fail here because we can't create implicit diffs for
10859 // unregistered images)
10860 Guid uuidRegistryParent;
10861 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10862 Assert(fInRegistry); NOREF(fInRegistry);
10863 rc = diff->init(mParent,
10864 pMedium->getPreferredDiffFormat(),
10865 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10866 uuidRegistryParent);
10867 if (FAILED(rc)) throw rc;
10868
10869 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10870 * the push_back? Looks like we're going to release medium with the
10871 * wrong kind of lock (general issue with if we fail anywhere at all)
10872 * and an orphaned VDI in the snapshots folder. */
10873
10874 /* update the appropriate lock list */
10875 MediumLockList *pMediumLockList;
10876 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10877 AssertComRCThrowRC(rc);
10878 if (aOnline)
10879 {
10880 alock.release();
10881 /* The currently attached medium will be read-only, change
10882 * the lock type to read. */
10883 rc = pMediumLockList->Update(pMedium, false);
10884 alock.acquire();
10885 AssertComRCThrowRC(rc);
10886 }
10887
10888 /* release the locks before the potentially lengthy operation */
10889 alock.release();
10890 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10891 pMediumLockList,
10892 NULL /* aProgress */,
10893 true /* aWait */);
10894 alock.acquire();
10895 if (FAILED(rc)) throw rc;
10896
10897 /* actual lock list update is done in Medium::commitMedia */
10898
10899 rc = diff->addBackReference(mData->mUuid);
10900 AssertComRCThrowRC(rc);
10901
10902 /* add a new attachment */
10903 ComObjPtr<MediumAttachment> attachment;
10904 attachment.createObject();
10905 rc = attachment->init(this,
10906 diff,
10907 pAtt->getControllerName(),
10908 pAtt->getPort(),
10909 pAtt->getDevice(),
10910 DeviceType_HardDisk,
10911 true /* aImplicit */,
10912 false /* aPassthrough */,
10913 false /* aTempEject */,
10914 pAtt->getNonRotational(),
10915 pAtt->getDiscard(),
10916 pAtt->getBandwidthGroup());
10917 if (FAILED(rc)) throw rc;
10918
10919 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10920 AssertComRCThrowRC(rc);
10921 mMediaData->mAttachments.push_back(attachment);
10922 }
10923 }
10924 catch (HRESULT aRC) { rc = aRC; }
10925
10926 /* unlock all hard disks we locked when there is no VM */
10927 if (!aOnline)
10928 {
10929 ErrorInfoKeeper eik;
10930
10931 HRESULT rc1 = lockedMediaMap->Clear();
10932 AssertComRC(rc1);
10933 }
10934
10935 return rc;
10936}
10937
10938/**
10939 * Deletes implicit differencing hard disks created either by
10940 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10941 *
10942 * Note that to delete hard disks created by #AttachDevice() this method is
10943 * called from #fixupMedia() when the changes are rolled back.
10944 *
10945 * @note Locks this object and the media tree for writing.
10946 */
10947HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10948{
10949 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10950
10951 AutoCaller autoCaller(this);
10952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10953
10954 AutoMultiWriteLock2 alock(this->lockHandle(),
10955 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10956
10957 /* We absolutely must have backed up state. */
10958 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10959
10960 /* Check if there are any implicitly created diff images. */
10961 bool fImplicitDiffs = false;
10962 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10963 it != mMediaData->mAttachments.end();
10964 ++it)
10965 {
10966 const ComObjPtr<MediumAttachment> &pAtt = *it;
10967 if (pAtt->isImplicit())
10968 {
10969 fImplicitDiffs = true;
10970 break;
10971 }
10972 }
10973 /* If there is nothing to do, leave early. This saves lots of image locking
10974 * effort. It also avoids a MachineStateChanged event without real reason.
10975 * This is important e.g. when loading a VM config, because there should be
10976 * no events. Otherwise API clients can become thoroughly confused for
10977 * inaccessible VMs (the code for loading VM configs uses this method for
10978 * cleanup if the config makes no sense), as they take such events as an
10979 * indication that the VM is alive, and they would force the VM config to
10980 * be reread, leading to an endless loop. */
10981 if (!fImplicitDiffs)
10982 return S_OK;
10983
10984 HRESULT rc = S_OK;
10985 MachineState_T oldState = mData->mMachineState;
10986
10987 /* will release the lock before the potentially lengthy operation,
10988 * so protect with the special state (unless already protected) */
10989 if ( oldState != MachineState_Saving
10990 && oldState != MachineState_LiveSnapshotting
10991 && oldState != MachineState_RestoringSnapshot
10992 && oldState != MachineState_DeletingSnapshot
10993 && oldState != MachineState_DeletingSnapshotOnline
10994 && oldState != MachineState_DeletingSnapshotPaused
10995 )
10996 setMachineState(MachineState_SettingUp);
10997
10998 // use appropriate locked media map (online or offline)
10999 MediumLockListMap lockedMediaOffline;
11000 MediumLockListMap *lockedMediaMap;
11001 if (aOnline)
11002 lockedMediaMap = &mData->mSession.mLockedMedia;
11003 else
11004 lockedMediaMap = &lockedMediaOffline;
11005
11006 try
11007 {
11008 if (!aOnline)
11009 {
11010 /* lock all attached hard disks early to detect "in use"
11011 * situations before deleting actual diffs */
11012 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11013 it != mMediaData->mAttachments.end();
11014 ++it)
11015 {
11016 MediumAttachment* pAtt = *it;
11017 if (pAtt->getType() == DeviceType_HardDisk)
11018 {
11019 Medium* pMedium = pAtt->getMedium();
11020 Assert(pMedium);
11021
11022 MediumLockList *pMediumLockList(new MediumLockList());
11023 alock.release();
11024 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11025 false /* fMediumLockWrite */,
11026 NULL,
11027 *pMediumLockList);
11028 alock.acquire();
11029
11030 if (FAILED(rc))
11031 {
11032 delete pMediumLockList;
11033 throw rc;
11034 }
11035
11036 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11037 if (FAILED(rc))
11038 throw rc;
11039 }
11040 }
11041
11042 if (FAILED(rc))
11043 throw rc;
11044 } // end of offline
11045
11046 /* Lock lists are now up to date and include implicitly created media */
11047
11048 /* Go through remembered attachments and delete all implicitly created
11049 * diffs and fix up the attachment information */
11050 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11051 MediaData::AttachmentList implicitAtts;
11052 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11053 it != mMediaData->mAttachments.end();
11054 ++it)
11055 {
11056 ComObjPtr<MediumAttachment> pAtt = *it;
11057 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11058 if (pMedium.isNull())
11059 continue;
11060
11061 // Implicit attachments go on the list for deletion and back references are removed.
11062 if (pAtt->isImplicit())
11063 {
11064 /* Deassociate and mark for deletion */
11065 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11066 rc = pMedium->removeBackReference(mData->mUuid);
11067 if (FAILED(rc))
11068 throw rc;
11069 implicitAtts.push_back(pAtt);
11070 continue;
11071 }
11072
11073 /* Was this medium attached before? */
11074 if (!findAttachment(oldAtts, pMedium))
11075 {
11076 /* no: de-associate */
11077 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11078 rc = pMedium->removeBackReference(mData->mUuid);
11079 if (FAILED(rc))
11080 throw rc;
11081 continue;
11082 }
11083 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11084 }
11085
11086 /* If there are implicit attachments to delete, throw away the lock
11087 * map contents (which will unlock all media) since the medium
11088 * attachments will be rolled back. Below we need to completely
11089 * recreate the lock map anyway since it is infinitely complex to
11090 * do this incrementally (would need reconstructing each attachment
11091 * change, which would be extremely hairy). */
11092 if (implicitAtts.size() != 0)
11093 {
11094 ErrorInfoKeeper eik;
11095
11096 HRESULT rc1 = lockedMediaMap->Clear();
11097 AssertComRC(rc1);
11098 }
11099
11100 /* rollback hard disk changes */
11101 mMediaData.rollback();
11102
11103 MultiResult mrc(S_OK);
11104
11105 // Delete unused implicit diffs.
11106 if (implicitAtts.size() != 0)
11107 {
11108 alock.release();
11109
11110 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11111 it != implicitAtts.end();
11112 ++it)
11113 {
11114 // Remove medium associated with this attachment.
11115 ComObjPtr<MediumAttachment> pAtt = *it;
11116 Assert(pAtt);
11117 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11118 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11119 Assert(pMedium);
11120
11121 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11122 // continue on delete failure, just collect error messages
11123 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11124 mrc = rc;
11125 }
11126
11127 alock.acquire();
11128
11129 /* if there is a VM recreate media lock map as mentioned above,
11130 * otherwise it is a waste of time and we leave things unlocked */
11131 if (aOnline)
11132 {
11133 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11134 /* must never be NULL, but better safe than sorry */
11135 if (!pMachine.isNull())
11136 {
11137 alock.release();
11138 rc = mData->mSession.mMachine->lockMedia();
11139 alock.acquire();
11140 if (FAILED(rc))
11141 throw rc;
11142 }
11143 }
11144 }
11145 }
11146 catch (HRESULT aRC) {rc = aRC;}
11147
11148 if (mData->mMachineState == MachineState_SettingUp)
11149 setMachineState(oldState);
11150
11151 /* unlock all hard disks we locked when there is no VM */
11152 if (!aOnline)
11153 {
11154 ErrorInfoKeeper eik;
11155
11156 HRESULT rc1 = lockedMediaMap->Clear();
11157 AssertComRC(rc1);
11158 }
11159
11160 return rc;
11161}
11162
11163
11164/**
11165 * Looks through the given list of media attachments for one with the given parameters
11166 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11167 * can be searched as well if needed.
11168 *
11169 * @param list
11170 * @param aControllerName
11171 * @param aControllerPort
11172 * @param aDevice
11173 * @return
11174 */
11175MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11176 IN_BSTR aControllerName,
11177 LONG aControllerPort,
11178 LONG aDevice)
11179{
11180 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11181 it != ll.end();
11182 ++it)
11183 {
11184 MediumAttachment *pAttach = *it;
11185 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11186 return pAttach;
11187 }
11188
11189 return NULL;
11190}
11191
11192/**
11193 * Looks through the given list of media attachments for one with the given parameters
11194 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11195 * can be searched as well if needed.
11196 *
11197 * @param list
11198 * @param aControllerName
11199 * @param aControllerPort
11200 * @param aDevice
11201 * @return
11202 */
11203MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11204 ComObjPtr<Medium> pMedium)
11205{
11206 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11207 it != ll.end();
11208 ++it)
11209 {
11210 MediumAttachment *pAttach = *it;
11211 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11212 if (pMediumThis == pMedium)
11213 return pAttach;
11214 }
11215
11216 return NULL;
11217}
11218
11219/**
11220 * Looks through the given list of media attachments for one with the given parameters
11221 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11222 * can be searched as well if needed.
11223 *
11224 * @param list
11225 * @param aControllerName
11226 * @param aControllerPort
11227 * @param aDevice
11228 * @return
11229 */
11230MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11231 Guid &id)
11232{
11233 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11234 it != ll.end();
11235 ++it)
11236 {
11237 MediumAttachment *pAttach = *it;
11238 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11239 if (pMediumThis->getId() == id)
11240 return pAttach;
11241 }
11242
11243 return NULL;
11244}
11245
11246/**
11247 * Main implementation for Machine::DetachDevice. This also gets called
11248 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11249 *
11250 * @param pAttach Medium attachment to detach.
11251 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11252 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11253 * @return
11254 */
11255HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11256 AutoWriteLock &writeLock,
11257 Snapshot *pSnapshot)
11258{
11259 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11260 DeviceType_T mediumType = pAttach->getType();
11261
11262 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11263
11264 if (pAttach->isImplicit())
11265 {
11266 /* attempt to implicitly delete the implicitly created diff */
11267
11268 /// @todo move the implicit flag from MediumAttachment to Medium
11269 /// and forbid any hard disk operation when it is implicit. Or maybe
11270 /// a special media state for it to make it even more simple.
11271
11272 Assert(mMediaData.isBackedUp());
11273
11274 /* will release the lock before the potentially lengthy operation, so
11275 * protect with the special state */
11276 MachineState_T oldState = mData->mMachineState;
11277 setMachineState(MachineState_SettingUp);
11278
11279 writeLock.release();
11280
11281 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11282 true /*aWait*/);
11283
11284 writeLock.acquire();
11285
11286 setMachineState(oldState);
11287
11288 if (FAILED(rc)) return rc;
11289 }
11290
11291 setModified(IsModified_Storage);
11292 mMediaData.backup();
11293 mMediaData->mAttachments.remove(pAttach);
11294
11295 if (!oldmedium.isNull())
11296 {
11297 // if this is from a snapshot, do not defer detachment to commitMedia()
11298 if (pSnapshot)
11299 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11300 // else if non-hard disk media, do not defer detachment to commitMedia() either
11301 else if (mediumType != DeviceType_HardDisk)
11302 oldmedium->removeBackReference(mData->mUuid);
11303 }
11304
11305 return S_OK;
11306}
11307
11308/**
11309 * Goes thru all media of the given list and
11310 *
11311 * 1) calls detachDevice() on each of them for this machine and
11312 * 2) adds all Medium objects found in the process to the given list,
11313 * depending on cleanupMode.
11314 *
11315 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11316 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11317 * media to the list.
11318 *
11319 * This gets called from Machine::Unregister, both for the actual Machine and
11320 * the SnapshotMachine objects that might be found in the snapshots.
11321 *
11322 * Requires caller and locking. The machine lock must be passed in because it
11323 * will be passed on to detachDevice which needs it for temporary unlocking.
11324 *
11325 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11326 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11327 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11328 * otherwise no media get added.
11329 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11330 * @return
11331 */
11332HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11333 Snapshot *pSnapshot,
11334 CleanupMode_T cleanupMode,
11335 MediaList &llMedia)
11336{
11337 Assert(isWriteLockOnCurrentThread());
11338
11339 HRESULT rc;
11340
11341 // make a temporary list because detachDevice invalidates iterators into
11342 // mMediaData->mAttachments
11343 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11344
11345 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11346 it != llAttachments2.end();
11347 ++it)
11348 {
11349 ComObjPtr<MediumAttachment> &pAttach = *it;
11350 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11351
11352 if (!pMedium.isNull())
11353 {
11354 AutoCaller mac(pMedium);
11355 if (FAILED(mac.rc())) return mac.rc();
11356 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11357 DeviceType_T devType = pMedium->getDeviceType();
11358 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11359 && devType == DeviceType_HardDisk)
11360 || (cleanupMode == CleanupMode_Full)
11361 )
11362 {
11363 llMedia.push_back(pMedium);
11364 ComObjPtr<Medium> pParent = pMedium->getParent();
11365 /*
11366 * Search for medias which are not attached to any machine, but
11367 * in the chain to an attached disk. Mediums are only consided
11368 * if they are:
11369 * - have only one child
11370 * - no references to any machines
11371 * - are of normal medium type
11372 */
11373 while (!pParent.isNull())
11374 {
11375 AutoCaller mac1(pParent);
11376 if (FAILED(mac1.rc())) return mac1.rc();
11377 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11378 if (pParent->getChildren().size() == 1)
11379 {
11380 if ( pParent->getMachineBackRefCount() == 0
11381 && pParent->getType() == MediumType_Normal
11382 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11383 llMedia.push_back(pParent);
11384 }
11385 else
11386 break;
11387 pParent = pParent->getParent();
11388 }
11389 }
11390 }
11391
11392 // real machine: then we need to use the proper method
11393 rc = detachDevice(pAttach, writeLock, pSnapshot);
11394
11395 if (FAILED(rc))
11396 return rc;
11397 }
11398
11399 return S_OK;
11400}
11401
11402/**
11403 * Perform deferred hard disk detachments.
11404 *
11405 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11406 * backed up).
11407 *
11408 * If @a aOnline is @c true then this method will also unlock the old hard disks
11409 * for which the new implicit diffs were created and will lock these new diffs for
11410 * writing.
11411 *
11412 * @param aOnline Whether the VM was online prior to this operation.
11413 *
11414 * @note Locks this object for writing!
11415 */
11416void Machine::commitMedia(bool aOnline /*= false*/)
11417{
11418 AutoCaller autoCaller(this);
11419 AssertComRCReturnVoid(autoCaller.rc());
11420
11421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11422
11423 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11424
11425 HRESULT rc = S_OK;
11426
11427 /* no attach/detach operations -- nothing to do */
11428 if (!mMediaData.isBackedUp())
11429 return;
11430
11431 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11432 bool fMediaNeedsLocking = false;
11433
11434 /* enumerate new attachments */
11435 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11436 it != mMediaData->mAttachments.end();
11437 ++it)
11438 {
11439 MediumAttachment *pAttach = *it;
11440
11441 pAttach->commit();
11442
11443 Medium* pMedium = pAttach->getMedium();
11444 bool fImplicit = pAttach->isImplicit();
11445
11446 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11447 (pMedium) ? pMedium->getName().c_str() : "NULL",
11448 fImplicit));
11449
11450 /** @todo convert all this Machine-based voodoo to MediumAttachment
11451 * based commit logic. */
11452 if (fImplicit)
11453 {
11454 /* convert implicit attachment to normal */
11455 pAttach->setImplicit(false);
11456
11457 if ( aOnline
11458 && pMedium
11459 && pAttach->getType() == DeviceType_HardDisk
11460 )
11461 {
11462 ComObjPtr<Medium> parent = pMedium->getParent();
11463 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11464
11465 /* update the appropriate lock list */
11466 MediumLockList *pMediumLockList;
11467 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11468 AssertComRC(rc);
11469 if (pMediumLockList)
11470 {
11471 /* unlock if there's a need to change the locking */
11472 if (!fMediaNeedsLocking)
11473 {
11474 rc = mData->mSession.mLockedMedia.Unlock();
11475 AssertComRC(rc);
11476 fMediaNeedsLocking = true;
11477 }
11478 rc = pMediumLockList->Update(parent, false);
11479 AssertComRC(rc);
11480 rc = pMediumLockList->Append(pMedium, true);
11481 AssertComRC(rc);
11482 }
11483 }
11484
11485 continue;
11486 }
11487
11488 if (pMedium)
11489 {
11490 /* was this medium attached before? */
11491 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11492 oldIt != oldAtts.end();
11493 ++oldIt)
11494 {
11495 MediumAttachment *pOldAttach = *oldIt;
11496 if (pOldAttach->getMedium() == pMedium)
11497 {
11498 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11499
11500 /* yes: remove from old to avoid de-association */
11501 oldAtts.erase(oldIt);
11502 break;
11503 }
11504 }
11505 }
11506 }
11507
11508 /* enumerate remaining old attachments and de-associate from the
11509 * current machine state */
11510 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11511 it != oldAtts.end();
11512 ++it)
11513 {
11514 MediumAttachment *pAttach = *it;
11515 Medium* pMedium = pAttach->getMedium();
11516
11517 /* Detach only hard disks, since DVD/floppy media is detached
11518 * instantly in MountMedium. */
11519 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11520 {
11521 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11522
11523 /* now de-associate from the current machine state */
11524 rc = pMedium->removeBackReference(mData->mUuid);
11525 AssertComRC(rc);
11526
11527 if (aOnline)
11528 {
11529 /* unlock since medium is not used anymore */
11530 MediumLockList *pMediumLockList;
11531 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11532 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11533 {
11534 /* this happens for online snapshots, there the attachment
11535 * is changing, but only to a diff image created under
11536 * the old one, so there is no separate lock list */
11537 Assert(!pMediumLockList);
11538 }
11539 else
11540 {
11541 AssertComRC(rc);
11542 if (pMediumLockList)
11543 {
11544 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11545 AssertComRC(rc);
11546 }
11547 }
11548 }
11549 }
11550 }
11551
11552 /* take media locks again so that the locking state is consistent */
11553 if (fMediaNeedsLocking)
11554 {
11555 Assert(aOnline);
11556 rc = mData->mSession.mLockedMedia.Lock();
11557 AssertComRC(rc);
11558 }
11559
11560 /* commit the hard disk changes */
11561 mMediaData.commit();
11562
11563 if (isSessionMachine())
11564 {
11565 /*
11566 * Update the parent machine to point to the new owner.
11567 * This is necessary because the stored parent will point to the
11568 * session machine otherwise and cause crashes or errors later
11569 * when the session machine gets invalid.
11570 */
11571 /** @todo Change the MediumAttachment class to behave like any other
11572 * class in this regard by creating peer MediumAttachment
11573 * objects for session machines and share the data with the peer
11574 * machine.
11575 */
11576 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11577 it != mMediaData->mAttachments.end();
11578 ++it)
11579 {
11580 (*it)->updateParentMachine(mPeer);
11581 }
11582
11583 /* attach new data to the primary machine and reshare it */
11584 mPeer->mMediaData.attach(mMediaData);
11585 }
11586
11587 return;
11588}
11589
11590/**
11591 * Perform deferred deletion of implicitly created diffs.
11592 *
11593 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11594 * backed up).
11595 *
11596 * @note Locks this object for writing!
11597 */
11598void Machine::rollbackMedia()
11599{
11600 AutoCaller autoCaller(this);
11601 AssertComRCReturnVoid(autoCaller.rc());
11602
11603 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11604 LogFlowThisFunc(("Entering rollbackMedia\n"));
11605
11606 HRESULT rc = S_OK;
11607
11608 /* no attach/detach operations -- nothing to do */
11609 if (!mMediaData.isBackedUp())
11610 return;
11611
11612 /* enumerate new attachments */
11613 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11614 it != mMediaData->mAttachments.end();
11615 ++it)
11616 {
11617 MediumAttachment *pAttach = *it;
11618 /* Fix up the backrefs for DVD/floppy media. */
11619 if (pAttach->getType() != DeviceType_HardDisk)
11620 {
11621 Medium* pMedium = pAttach->getMedium();
11622 if (pMedium)
11623 {
11624 rc = pMedium->removeBackReference(mData->mUuid);
11625 AssertComRC(rc);
11626 }
11627 }
11628
11629 (*it)->rollback();
11630
11631 pAttach = *it;
11632 /* Fix up the backrefs for DVD/floppy media. */
11633 if (pAttach->getType() != DeviceType_HardDisk)
11634 {
11635 Medium* pMedium = pAttach->getMedium();
11636 if (pMedium)
11637 {
11638 rc = pMedium->addBackReference(mData->mUuid);
11639 AssertComRC(rc);
11640 }
11641 }
11642 }
11643
11644 /** @todo convert all this Machine-based voodoo to MediumAttachment
11645 * based rollback logic. */
11646 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11647
11648 return;
11649}
11650
11651/**
11652 * Returns true if the settings file is located in the directory named exactly
11653 * as the machine; this means, among other things, that the machine directory
11654 * should be auto-renamed.
11655 *
11656 * @param aSettingsDir if not NULL, the full machine settings file directory
11657 * name will be assigned there.
11658 *
11659 * @note Doesn't lock anything.
11660 * @note Not thread safe (must be called from this object's lock).
11661 */
11662bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11663{
11664 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11665 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11666 if (aSettingsDir)
11667 *aSettingsDir = strMachineDirName;
11668 strMachineDirName.stripPath(); // vmname
11669 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11670 strConfigFileOnly.stripPath() // vmname.vbox
11671 .stripExt(); // vmname
11672 /** @todo hack, make somehow use of ComposeMachineFilename */
11673 if (mUserData->s.fDirectoryIncludesUUID)
11674 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11675
11676 AssertReturn(!strMachineDirName.isEmpty(), false);
11677 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11678
11679 return strMachineDirName == strConfigFileOnly;
11680}
11681
11682/**
11683 * Discards all changes to machine settings.
11684 *
11685 * @param aNotify Whether to notify the direct session about changes or not.
11686 *
11687 * @note Locks objects for writing!
11688 */
11689void Machine::rollback(bool aNotify)
11690{
11691 AutoCaller autoCaller(this);
11692 AssertComRCReturn(autoCaller.rc(), (void)0);
11693
11694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11695
11696 if (!mStorageControllers.isNull())
11697 {
11698 if (mStorageControllers.isBackedUp())
11699 {
11700 /* unitialize all new devices (absent in the backed up list). */
11701 StorageControllerList::const_iterator it = mStorageControllers->begin();
11702 StorageControllerList *backedList = mStorageControllers.backedUpData();
11703 while (it != mStorageControllers->end())
11704 {
11705 if ( std::find(backedList->begin(), backedList->end(), *it)
11706 == backedList->end()
11707 )
11708 {
11709 (*it)->uninit();
11710 }
11711 ++it;
11712 }
11713
11714 /* restore the list */
11715 mStorageControllers.rollback();
11716 }
11717
11718 /* rollback any changes to devices after restoring the list */
11719 if (mData->flModifications & IsModified_Storage)
11720 {
11721 StorageControllerList::const_iterator it = mStorageControllers->begin();
11722 while (it != mStorageControllers->end())
11723 {
11724 (*it)->rollback();
11725 ++it;
11726 }
11727 }
11728 }
11729
11730 mUserData.rollback();
11731
11732 mHWData.rollback();
11733
11734 if (mData->flModifications & IsModified_Storage)
11735 rollbackMedia();
11736
11737 if (mBIOSSettings)
11738 mBIOSSettings->rollback();
11739
11740 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11741 mVRDEServer->rollback();
11742
11743 if (mAudioAdapter)
11744 mAudioAdapter->rollback();
11745
11746 if (mUSBController && (mData->flModifications & IsModified_USB))
11747 mUSBController->rollback();
11748
11749 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11750 mBandwidthControl->rollback();
11751
11752 if (!mHWData.isNull())
11753 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11754 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11755 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11756 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11757
11758 if (mData->flModifications & IsModified_NetworkAdapters)
11759 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11760 if ( mNetworkAdapters[slot]
11761 && mNetworkAdapters[slot]->isModified())
11762 {
11763 mNetworkAdapters[slot]->rollback();
11764 networkAdapters[slot] = mNetworkAdapters[slot];
11765 }
11766
11767 if (mData->flModifications & IsModified_SerialPorts)
11768 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11769 if ( mSerialPorts[slot]
11770 && mSerialPorts[slot]->isModified())
11771 {
11772 mSerialPorts[slot]->rollback();
11773 serialPorts[slot] = mSerialPorts[slot];
11774 }
11775
11776 if (mData->flModifications & IsModified_ParallelPorts)
11777 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11778 if ( mParallelPorts[slot]
11779 && mParallelPorts[slot]->isModified())
11780 {
11781 mParallelPorts[slot]->rollback();
11782 parallelPorts[slot] = mParallelPorts[slot];
11783 }
11784
11785 if (aNotify)
11786 {
11787 /* inform the direct session about changes */
11788
11789 ComObjPtr<Machine> that = this;
11790 uint32_t flModifications = mData->flModifications;
11791 alock.release();
11792
11793 if (flModifications & IsModified_SharedFolders)
11794 that->onSharedFolderChange();
11795
11796 if (flModifications & IsModified_VRDEServer)
11797 that->onVRDEServerChange(/* aRestart */ TRUE);
11798 if (flModifications & IsModified_USB)
11799 that->onUSBControllerChange();
11800
11801 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11802 if (networkAdapters[slot])
11803 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11804 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11805 if (serialPorts[slot])
11806 that->onSerialPortChange(serialPorts[slot]);
11807 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11808 if (parallelPorts[slot])
11809 that->onParallelPortChange(parallelPorts[slot]);
11810
11811 if (flModifications & IsModified_Storage)
11812 that->onStorageControllerChange();
11813
11814#if 0
11815 if (flModifications & IsModified_BandwidthControl)
11816 that->onBandwidthControlChange();
11817#endif
11818 }
11819}
11820
11821/**
11822 * Commits all the changes to machine settings.
11823 *
11824 * Note that this operation is supposed to never fail.
11825 *
11826 * @note Locks this object and children for writing.
11827 */
11828void Machine::commit()
11829{
11830 AutoCaller autoCaller(this);
11831 AssertComRCReturnVoid(autoCaller.rc());
11832
11833 AutoCaller peerCaller(mPeer);
11834 AssertComRCReturnVoid(peerCaller.rc());
11835
11836 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11837
11838 /*
11839 * use safe commit to ensure Snapshot machines (that share mUserData)
11840 * will still refer to a valid memory location
11841 */
11842 mUserData.commitCopy();
11843
11844 mHWData.commit();
11845
11846 if (mMediaData.isBackedUp())
11847 commitMedia(Global::IsOnline(mData->mMachineState));
11848
11849 mBIOSSettings->commit();
11850 mVRDEServer->commit();
11851 mAudioAdapter->commit();
11852 mUSBController->commit();
11853 mBandwidthControl->commit();
11854
11855 /* Since mNetworkAdapters is a list which might have been changed (resized)
11856 * without using the Backupable<> template we need to handle the copying
11857 * of the list entries manually, including the creation of peers for the
11858 * new objects. */
11859 bool commitNetworkAdapters = false;
11860 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11861 if (mPeer)
11862 {
11863 /* commit everything, even the ones which will go away */
11864 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11865 mNetworkAdapters[slot]->commit();
11866 /* copy over the new entries, creating a peer and uninit the original */
11867 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11868 for (size_t slot = 0; slot < newSize; slot++)
11869 {
11870 /* look if this adapter has a peer device */
11871 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11872 if (!peer)
11873 {
11874 /* no peer means the adapter is a newly created one;
11875 * create a peer owning data this data share it with */
11876 peer.createObject();
11877 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11878 }
11879 mPeer->mNetworkAdapters[slot] = peer;
11880 }
11881 /* uninit any no longer needed network adapters */
11882 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11883 mNetworkAdapters[slot]->uninit();
11884 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11885 {
11886 if (mPeer->mNetworkAdapters[slot])
11887 mPeer->mNetworkAdapters[slot]->uninit();
11888 }
11889 /* Keep the original network adapter count until this point, so that
11890 * discarding a chipset type change will not lose settings. */
11891 mNetworkAdapters.resize(newSize);
11892 mPeer->mNetworkAdapters.resize(newSize);
11893 }
11894 else
11895 {
11896 /* we have no peer (our parent is the newly created machine);
11897 * just commit changes to the network adapters */
11898 commitNetworkAdapters = true;
11899 }
11900 if (commitNetworkAdapters)
11901 {
11902 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11903 mNetworkAdapters[slot]->commit();
11904 }
11905
11906 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11907 mSerialPorts[slot]->commit();
11908 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11909 mParallelPorts[slot]->commit();
11910
11911 bool commitStorageControllers = false;
11912
11913 if (mStorageControllers.isBackedUp())
11914 {
11915 mStorageControllers.commit();
11916
11917 if (mPeer)
11918 {
11919 /* Commit all changes to new controllers (this will reshare data with
11920 * peers for those who have peers) */
11921 StorageControllerList *newList = new StorageControllerList();
11922 StorageControllerList::const_iterator it = mStorageControllers->begin();
11923 while (it != mStorageControllers->end())
11924 {
11925 (*it)->commit();
11926
11927 /* look if this controller has a peer device */
11928 ComObjPtr<StorageController> peer = (*it)->getPeer();
11929 if (!peer)
11930 {
11931 /* no peer means the device is a newly created one;
11932 * create a peer owning data this device share it with */
11933 peer.createObject();
11934 peer->init(mPeer, *it, true /* aReshare */);
11935 }
11936 else
11937 {
11938 /* remove peer from the old list */
11939 mPeer->mStorageControllers->remove(peer);
11940 }
11941 /* and add it to the new list */
11942 newList->push_back(peer);
11943
11944 ++it;
11945 }
11946
11947 /* uninit old peer's controllers that are left */
11948 it = mPeer->mStorageControllers->begin();
11949 while (it != mPeer->mStorageControllers->end())
11950 {
11951 (*it)->uninit();
11952 ++it;
11953 }
11954
11955 /* attach new list of controllers to our peer */
11956 mPeer->mStorageControllers.attach(newList);
11957 }
11958 else
11959 {
11960 /* we have no peer (our parent is the newly created machine);
11961 * just commit changes to devices */
11962 commitStorageControllers = true;
11963 }
11964 }
11965 else
11966 {
11967 /* the list of controllers itself is not changed,
11968 * just commit changes to controllers themselves */
11969 commitStorageControllers = true;
11970 }
11971
11972 if (commitStorageControllers)
11973 {
11974 StorageControllerList::const_iterator it = mStorageControllers->begin();
11975 while (it != mStorageControllers->end())
11976 {
11977 (*it)->commit();
11978 ++it;
11979 }
11980 }
11981
11982 if (isSessionMachine())
11983 {
11984 /* attach new data to the primary machine and reshare it */
11985 mPeer->mUserData.attach(mUserData);
11986 mPeer->mHWData.attach(mHWData);
11987 /* mMediaData is reshared by fixupMedia */
11988 // mPeer->mMediaData.attach(mMediaData);
11989 Assert(mPeer->mMediaData.data() == mMediaData.data());
11990 }
11991}
11992
11993/**
11994 * Copies all the hardware data from the given machine.
11995 *
11996 * Currently, only called when the VM is being restored from a snapshot. In
11997 * particular, this implies that the VM is not running during this method's
11998 * call.
11999 *
12000 * @note This method must be called from under this object's lock.
12001 *
12002 * @note This method doesn't call #commit(), so all data remains backed up and
12003 * unsaved.
12004 */
12005void Machine::copyFrom(Machine *aThat)
12006{
12007 AssertReturnVoid(!isSnapshotMachine());
12008 AssertReturnVoid(aThat->isSnapshotMachine());
12009
12010 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12011
12012 mHWData.assignCopy(aThat->mHWData);
12013
12014 // create copies of all shared folders (mHWData after attaching a copy
12015 // contains just references to original objects)
12016 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12017 it != mHWData->mSharedFolders.end();
12018 ++it)
12019 {
12020 ComObjPtr<SharedFolder> folder;
12021 folder.createObject();
12022 HRESULT rc = folder->initCopy(getMachine(), *it);
12023 AssertComRC(rc);
12024 *it = folder;
12025 }
12026
12027 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12028 mVRDEServer->copyFrom(aThat->mVRDEServer);
12029 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12030 mUSBController->copyFrom(aThat->mUSBController);
12031 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12032
12033 /* create private copies of all controllers */
12034 mStorageControllers.backup();
12035 mStorageControllers->clear();
12036 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12037 it != aThat->mStorageControllers->end();
12038 ++it)
12039 {
12040 ComObjPtr<StorageController> ctrl;
12041 ctrl.createObject();
12042 ctrl->initCopy(this, *it);
12043 mStorageControllers->push_back(ctrl);
12044 }
12045
12046 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12047 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12048 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12049 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12050 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12051 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12052 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12053}
12054
12055/**
12056 * Returns whether the given storage controller is hotplug capable.
12057 *
12058 * @returns true if the controller supports hotplugging
12059 * false otherwise.
12060 * @param enmCtrlType The controller type to check for.
12061 */
12062bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12063{
12064 switch (enmCtrlType)
12065 {
12066 case StorageControllerType_IntelAhci:
12067 return true;
12068 case StorageControllerType_LsiLogic:
12069 case StorageControllerType_LsiLogicSas:
12070 case StorageControllerType_BusLogic:
12071 case StorageControllerType_PIIX3:
12072 case StorageControllerType_PIIX4:
12073 case StorageControllerType_ICH6:
12074 case StorageControllerType_I82078:
12075 default:
12076 return false;
12077 }
12078}
12079
12080#ifdef VBOX_WITH_RESOURCE_USAGE_API
12081
12082void Machine::getDiskList(MediaList &list)
12083{
12084 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12085 it != mMediaData->mAttachments.end();
12086 ++it)
12087 {
12088 MediumAttachment* pAttach = *it;
12089 /* just in case */
12090 AssertStmt(pAttach, continue);
12091
12092 AutoCaller localAutoCallerA(pAttach);
12093 if (FAILED(localAutoCallerA.rc())) continue;
12094
12095 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12096
12097 if (pAttach->getType() == DeviceType_HardDisk)
12098 list.push_back(pAttach->getMedium());
12099 }
12100}
12101
12102void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12103{
12104 AssertReturnVoid(isWriteLockOnCurrentThread());
12105 AssertPtrReturnVoid(aCollector);
12106
12107 pm::CollectorHAL *hal = aCollector->getHAL();
12108 /* Create sub metrics */
12109 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12110 "Percentage of processor time spent in user mode by the VM process.");
12111 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12112 "Percentage of processor time spent in kernel mode by the VM process.");
12113 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12114 "Size of resident portion of VM process in memory.");
12115 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12116 "Actual size of all VM disks combined.");
12117 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12118 "Network receive rate.");
12119 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12120 "Network transmit rate.");
12121 /* Create and register base metrics */
12122 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12123 cpuLoadUser, cpuLoadKernel);
12124 aCollector->registerBaseMetric(cpuLoad);
12125 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12126 ramUsageUsed);
12127 aCollector->registerBaseMetric(ramUsage);
12128 MediaList disks;
12129 getDiskList(disks);
12130 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12131 diskUsageUsed);
12132 aCollector->registerBaseMetric(diskUsage);
12133
12134 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12135 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12136 new pm::AggregateAvg()));
12137 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12138 new pm::AggregateMin()));
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12140 new pm::AggregateMax()));
12141 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12142 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12143 new pm::AggregateAvg()));
12144 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12145 new pm::AggregateMin()));
12146 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12147 new pm::AggregateMax()));
12148
12149 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12150 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12151 new pm::AggregateAvg()));
12152 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12153 new pm::AggregateMin()));
12154 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12155 new pm::AggregateMax()));
12156
12157 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12158 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12159 new pm::AggregateAvg()));
12160 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12161 new pm::AggregateMin()));
12162 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12163 new pm::AggregateMax()));
12164
12165
12166 /* Guest metrics collector */
12167 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12168 aCollector->registerGuest(mCollectorGuest);
12169 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12170 this, __PRETTY_FUNCTION__, mCollectorGuest));
12171
12172 /* Create sub metrics */
12173 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12174 "Percentage of processor time spent in user mode as seen by the guest.");
12175 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12176 "Percentage of processor time spent in kernel mode as seen by the guest.");
12177 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12178 "Percentage of processor time spent idling as seen by the guest.");
12179
12180 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12181 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12182 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12183 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12184 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12185 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12186
12187 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12188
12189 /* Create and register base metrics */
12190 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12191 machineNetRx, machineNetTx);
12192 aCollector->registerBaseMetric(machineNetRate);
12193
12194 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12195 guestLoadUser, guestLoadKernel, guestLoadIdle);
12196 aCollector->registerBaseMetric(guestCpuLoad);
12197
12198 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12199 guestMemTotal, guestMemFree,
12200 guestMemBalloon, guestMemShared,
12201 guestMemCache, guestPagedTotal);
12202 aCollector->registerBaseMetric(guestCpuMem);
12203
12204 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12205 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12206 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12207 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12208
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12211 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12213
12214 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12215 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12216 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12218
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12221 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12223
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12226 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12228
12229 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12233
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12238
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12248
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12253
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12258}
12259
12260void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12261{
12262 AssertReturnVoid(isWriteLockOnCurrentThread());
12263
12264 if (aCollector)
12265 {
12266 aCollector->unregisterMetricsFor(aMachine);
12267 aCollector->unregisterBaseMetricsFor(aMachine);
12268 }
12269}
12270
12271#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12272
12273
12274////////////////////////////////////////////////////////////////////////////////
12275
12276DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12277
12278HRESULT SessionMachine::FinalConstruct()
12279{
12280 LogFlowThisFunc(("\n"));
12281
12282#if defined(RT_OS_WINDOWS)
12283 mIPCSem = NULL;
12284#elif defined(RT_OS_OS2)
12285 mIPCSem = NULLHANDLE;
12286#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12287 mIPCSem = -1;
12288#else
12289# error "Port me!"
12290#endif
12291
12292 return BaseFinalConstruct();
12293}
12294
12295void SessionMachine::FinalRelease()
12296{
12297 LogFlowThisFunc(("\n"));
12298
12299 uninit(Uninit::Unexpected);
12300
12301 BaseFinalRelease();
12302}
12303
12304/**
12305 * @note Must be called only by Machine::openSession() from its own write lock.
12306 */
12307HRESULT SessionMachine::init(Machine *aMachine)
12308{
12309 LogFlowThisFuncEnter();
12310 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12311
12312 AssertReturn(aMachine, E_INVALIDARG);
12313
12314 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12315
12316 /* Enclose the state transition NotReady->InInit->Ready */
12317 AutoInitSpan autoInitSpan(this);
12318 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12319
12320 /* create the interprocess semaphore */
12321#if defined(RT_OS_WINDOWS)
12322 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12323 for (size_t i = 0; i < mIPCSemName.length(); i++)
12324 if (mIPCSemName.raw()[i] == '\\')
12325 mIPCSemName.raw()[i] = '/';
12326 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12327 ComAssertMsgRet(mIPCSem,
12328 ("Cannot create IPC mutex '%ls', err=%d",
12329 mIPCSemName.raw(), ::GetLastError()),
12330 E_FAIL);
12331#elif defined(RT_OS_OS2)
12332 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12333 aMachine->mData->mUuid.raw());
12334 mIPCSemName = ipcSem;
12335 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12336 ComAssertMsgRet(arc == NO_ERROR,
12337 ("Cannot create IPC mutex '%s', arc=%ld",
12338 ipcSem.c_str(), arc),
12339 E_FAIL);
12340#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12341# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12342# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12343 /** @todo Check that this still works correctly. */
12344 AssertCompileSize(key_t, 8);
12345# else
12346 AssertCompileSize(key_t, 4);
12347# endif
12348 key_t key;
12349 mIPCSem = -1;
12350 mIPCKey = "0";
12351 for (uint32_t i = 0; i < 1 << 24; i++)
12352 {
12353 key = ((uint32_t)'V' << 24) | i;
12354 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12355 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12356 {
12357 mIPCSem = sem;
12358 if (sem >= 0)
12359 mIPCKey = BstrFmt("%u", key);
12360 break;
12361 }
12362 }
12363# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12364 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12365 char *pszSemName = NULL;
12366 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12367 key_t key = ::ftok(pszSemName, 'V');
12368 RTStrFree(pszSemName);
12369
12370 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12371# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12372
12373 int errnoSave = errno;
12374 if (mIPCSem < 0 && errnoSave == ENOSYS)
12375 {
12376 setError(E_FAIL,
12377 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12378 "support for SysV IPC. Check the host kernel configuration for "
12379 "CONFIG_SYSVIPC=y"));
12380 return E_FAIL;
12381 }
12382 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12383 * the IPC semaphores */
12384 if (mIPCSem < 0 && errnoSave == ENOSPC)
12385 {
12386#ifdef RT_OS_LINUX
12387 setError(E_FAIL,
12388 tr("Cannot create IPC semaphore because the system limit for the "
12389 "maximum number of semaphore sets (SEMMNI), or the system wide "
12390 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12391 "current set of SysV IPC semaphores can be determined from "
12392 "the file /proc/sysvipc/sem"));
12393#else
12394 setError(E_FAIL,
12395 tr("Cannot create IPC semaphore because the system-imposed limit "
12396 "on the maximum number of allowed semaphores or semaphore "
12397 "identifiers system-wide would be exceeded"));
12398#endif
12399 return E_FAIL;
12400 }
12401 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12402 E_FAIL);
12403 /* set the initial value to 1 */
12404 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12405 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12406 E_FAIL);
12407#else
12408# error "Port me!"
12409#endif
12410
12411 /* memorize the peer Machine */
12412 unconst(mPeer) = aMachine;
12413 /* share the parent pointer */
12414 unconst(mParent) = aMachine->mParent;
12415
12416 /* take the pointers to data to share */
12417 mData.share(aMachine->mData);
12418 mSSData.share(aMachine->mSSData);
12419
12420 mUserData.share(aMachine->mUserData);
12421 mHWData.share(aMachine->mHWData);
12422 mMediaData.share(aMachine->mMediaData);
12423
12424 mStorageControllers.allocate();
12425 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12426 it != aMachine->mStorageControllers->end();
12427 ++it)
12428 {
12429 ComObjPtr<StorageController> ctl;
12430 ctl.createObject();
12431 ctl->init(this, *it);
12432 mStorageControllers->push_back(ctl);
12433 }
12434
12435 unconst(mBIOSSettings).createObject();
12436 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12437 /* create another VRDEServer object that will be mutable */
12438 unconst(mVRDEServer).createObject();
12439 mVRDEServer->init(this, aMachine->mVRDEServer);
12440 /* create another audio adapter object that will be mutable */
12441 unconst(mAudioAdapter).createObject();
12442 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12443 /* create a list of serial ports that will be mutable */
12444 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12445 {
12446 unconst(mSerialPorts[slot]).createObject();
12447 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12448 }
12449 /* create a list of parallel ports that will be mutable */
12450 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12451 {
12452 unconst(mParallelPorts[slot]).createObject();
12453 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12454 }
12455 /* create another USB controller object that will be mutable */
12456 unconst(mUSBController).createObject();
12457 mUSBController->init(this, aMachine->mUSBController);
12458
12459 /* create a list of network adapters that will be mutable */
12460 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12461 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12462 {
12463 unconst(mNetworkAdapters[slot]).createObject();
12464 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12465 }
12466
12467 /* create another bandwidth control object that will be mutable */
12468 unconst(mBandwidthControl).createObject();
12469 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12470
12471 /* default is to delete saved state on Saved -> PoweredOff transition */
12472 mRemoveSavedState = true;
12473
12474 /* Confirm a successful initialization when it's the case */
12475 autoInitSpan.setSucceeded();
12476
12477 LogFlowThisFuncLeave();
12478 return S_OK;
12479}
12480
12481/**
12482 * Uninitializes this session object. If the reason is other than
12483 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12484 *
12485 * @param aReason uninitialization reason
12486 *
12487 * @note Locks mParent + this object for writing.
12488 */
12489void SessionMachine::uninit(Uninit::Reason aReason)
12490{
12491 LogFlowThisFuncEnter();
12492 LogFlowThisFunc(("reason=%d\n", aReason));
12493
12494 /*
12495 * Strongly reference ourselves to prevent this object deletion after
12496 * mData->mSession.mMachine.setNull() below (which can release the last
12497 * reference and call the destructor). Important: this must be done before
12498 * accessing any members (and before AutoUninitSpan that does it as well).
12499 * This self reference will be released as the very last step on return.
12500 */
12501 ComObjPtr<SessionMachine> selfRef = this;
12502
12503 /* Enclose the state transition Ready->InUninit->NotReady */
12504 AutoUninitSpan autoUninitSpan(this);
12505 if (autoUninitSpan.uninitDone())
12506 {
12507 LogFlowThisFunc(("Already uninitialized\n"));
12508 LogFlowThisFuncLeave();
12509 return;
12510 }
12511
12512 if (autoUninitSpan.initFailed())
12513 {
12514 /* We've been called by init() because it's failed. It's not really
12515 * necessary (nor it's safe) to perform the regular uninit sequence
12516 * below, the following is enough.
12517 */
12518 LogFlowThisFunc(("Initialization failed.\n"));
12519#if defined(RT_OS_WINDOWS)
12520 if (mIPCSem)
12521 ::CloseHandle(mIPCSem);
12522 mIPCSem = NULL;
12523#elif defined(RT_OS_OS2)
12524 if (mIPCSem != NULLHANDLE)
12525 ::DosCloseMutexSem(mIPCSem);
12526 mIPCSem = NULLHANDLE;
12527#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12528 if (mIPCSem >= 0)
12529 ::semctl(mIPCSem, 0, IPC_RMID);
12530 mIPCSem = -1;
12531# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12532 mIPCKey = "0";
12533# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12534#else
12535# error "Port me!"
12536#endif
12537 uninitDataAndChildObjects();
12538 mData.free();
12539 unconst(mParent) = NULL;
12540 unconst(mPeer) = NULL;
12541 LogFlowThisFuncLeave();
12542 return;
12543 }
12544
12545 MachineState_T lastState;
12546 {
12547 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12548 lastState = mData->mMachineState;
12549 }
12550 NOREF(lastState);
12551
12552#ifdef VBOX_WITH_USB
12553 // release all captured USB devices, but do this before requesting the locks below
12554 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12555 {
12556 /* Console::captureUSBDevices() is called in the VM process only after
12557 * setting the machine state to Starting or Restoring.
12558 * Console::detachAllUSBDevices() will be called upon successful
12559 * termination. So, we need to release USB devices only if there was
12560 * an abnormal termination of a running VM.
12561 *
12562 * This is identical to SessionMachine::DetachAllUSBDevices except
12563 * for the aAbnormal argument. */
12564 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12565 AssertComRC(rc);
12566 NOREF(rc);
12567
12568 USBProxyService *service = mParent->host()->usbProxyService();
12569 if (service)
12570 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12571 }
12572#endif /* VBOX_WITH_USB */
12573
12574 // we need to lock this object in uninit() because the lock is shared
12575 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12576 // and others need mParent lock, and USB needs host lock.
12577 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12578
12579#if 0
12580 // Trigger async cleanup tasks, avoid doing things here which are not
12581 // vital to be done immediately and maybe need more locks. This calls
12582 // Machine::unregisterMetrics().
12583 mParent->onMachineUninit(mPeer);
12584#else
12585 /*
12586 * It is safe to call Machine::unregisterMetrics() here because
12587 * PerformanceCollector::samplerCallback no longer accesses guest methods
12588 * holding the lock.
12589 */
12590 unregisterMetrics(mParent->performanceCollector(), mPeer);
12591#endif
12592 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12593 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12594 this, __PRETTY_FUNCTION__, mCollectorGuest));
12595 if (mCollectorGuest)
12596 {
12597 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12598 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12599 mCollectorGuest = NULL;
12600 }
12601
12602 if (aReason == Uninit::Abnormal)
12603 {
12604 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12605 Global::IsOnlineOrTransient(lastState)));
12606
12607 /* reset the state to Aborted */
12608 if (mData->mMachineState != MachineState_Aborted)
12609 setMachineState(MachineState_Aborted);
12610 }
12611
12612 // any machine settings modified?
12613 if (mData->flModifications)
12614 {
12615 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12616 rollback(false /* aNotify */);
12617 }
12618
12619 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12620 || !mConsoleTaskData.mSnapshot);
12621 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12622 {
12623 LogWarningThisFunc(("canceling failed save state request!\n"));
12624 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12625 }
12626 else if (!mConsoleTaskData.mSnapshot.isNull())
12627 {
12628 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12629
12630 /* delete all differencing hard disks created (this will also attach
12631 * their parents back by rolling back mMediaData) */
12632 rollbackMedia();
12633
12634 // delete the saved state file (it might have been already created)
12635 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12636 // think it's still in use
12637 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12638 mConsoleTaskData.mSnapshot->uninit();
12639 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12640 }
12641
12642 if (!mData->mSession.mType.isEmpty())
12643 {
12644 /* mType is not null when this machine's process has been started by
12645 * Machine::LaunchVMProcess(), therefore it is our child. We
12646 * need to queue the PID to reap the process (and avoid zombies on
12647 * Linux). */
12648 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12649 mParent->addProcessToReap(mData->mSession.mPID);
12650 }
12651
12652 mData->mSession.mPID = NIL_RTPROCESS;
12653
12654 if (aReason == Uninit::Unexpected)
12655 {
12656 /* Uninitialization didn't come from #checkForDeath(), so tell the
12657 * client watcher thread to update the set of machines that have open
12658 * sessions. */
12659 mParent->updateClientWatcher();
12660 }
12661
12662 /* uninitialize all remote controls */
12663 if (mData->mSession.mRemoteControls.size())
12664 {
12665 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12666 mData->mSession.mRemoteControls.size()));
12667
12668 Data::Session::RemoteControlList::iterator it =
12669 mData->mSession.mRemoteControls.begin();
12670 while (it != mData->mSession.mRemoteControls.end())
12671 {
12672 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12673 HRESULT rc = (*it)->Uninitialize();
12674 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12675 if (FAILED(rc))
12676 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12677 ++it;
12678 }
12679 mData->mSession.mRemoteControls.clear();
12680 }
12681
12682 /*
12683 * An expected uninitialization can come only from #checkForDeath().
12684 * Otherwise it means that something's gone really wrong (for example,
12685 * the Session implementation has released the VirtualBox reference
12686 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12687 * etc). However, it's also possible, that the client releases the IPC
12688 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12689 * but the VirtualBox release event comes first to the server process.
12690 * This case is practically possible, so we should not assert on an
12691 * unexpected uninit, just log a warning.
12692 */
12693
12694 if ((aReason == Uninit::Unexpected))
12695 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12696
12697 if (aReason != Uninit::Normal)
12698 {
12699 mData->mSession.mDirectControl.setNull();
12700 }
12701 else
12702 {
12703 /* this must be null here (see #OnSessionEnd()) */
12704 Assert(mData->mSession.mDirectControl.isNull());
12705 Assert(mData->mSession.mState == SessionState_Unlocking);
12706 Assert(!mData->mSession.mProgress.isNull());
12707 }
12708 if (mData->mSession.mProgress)
12709 {
12710 if (aReason == Uninit::Normal)
12711 mData->mSession.mProgress->notifyComplete(S_OK);
12712 else
12713 mData->mSession.mProgress->notifyComplete(E_FAIL,
12714 COM_IIDOF(ISession),
12715 getComponentName(),
12716 tr("The VM session was aborted"));
12717 mData->mSession.mProgress.setNull();
12718 }
12719
12720 /* remove the association between the peer machine and this session machine */
12721 Assert( (SessionMachine*)mData->mSession.mMachine == this
12722 || aReason == Uninit::Unexpected);
12723
12724 /* reset the rest of session data */
12725 mData->mSession.mMachine.setNull();
12726 mData->mSession.mState = SessionState_Unlocked;
12727 mData->mSession.mType.setNull();
12728
12729 /* close the interprocess semaphore before leaving the exclusive lock */
12730#if defined(RT_OS_WINDOWS)
12731 if (mIPCSem)
12732 ::CloseHandle(mIPCSem);
12733 mIPCSem = NULL;
12734#elif defined(RT_OS_OS2)
12735 if (mIPCSem != NULLHANDLE)
12736 ::DosCloseMutexSem(mIPCSem);
12737 mIPCSem = NULLHANDLE;
12738#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12739 if (mIPCSem >= 0)
12740 ::semctl(mIPCSem, 0, IPC_RMID);
12741 mIPCSem = -1;
12742# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12743 mIPCKey = "0";
12744# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12745#else
12746# error "Port me!"
12747#endif
12748
12749 /* fire an event */
12750 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12751
12752 uninitDataAndChildObjects();
12753
12754 /* free the essential data structure last */
12755 mData.free();
12756
12757 /* release the exclusive lock before setting the below two to NULL */
12758 multilock.release();
12759
12760 unconst(mParent) = NULL;
12761 unconst(mPeer) = NULL;
12762
12763 LogFlowThisFuncLeave();
12764}
12765
12766// util::Lockable interface
12767////////////////////////////////////////////////////////////////////////////////
12768
12769/**
12770 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12771 * with the primary Machine instance (mPeer).
12772 */
12773RWLockHandle *SessionMachine::lockHandle() const
12774{
12775 AssertReturn(mPeer != NULL, NULL);
12776 return mPeer->lockHandle();
12777}
12778
12779// IInternalMachineControl methods
12780////////////////////////////////////////////////////////////////////////////////
12781
12782/**
12783 * Passes collected guest statistics to performance collector object
12784 */
12785STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12786 ULONG aCpuKernel, ULONG aCpuIdle,
12787 ULONG aMemTotal, ULONG aMemFree,
12788 ULONG aMemBalloon, ULONG aMemShared,
12789 ULONG aMemCache, ULONG aPageTotal,
12790 ULONG aAllocVMM, ULONG aFreeVMM,
12791 ULONG aBalloonedVMM, ULONG aSharedVMM,
12792 ULONG aVmNetRx, ULONG aVmNetTx)
12793{
12794 if (mCollectorGuest)
12795 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12796 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12797 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12798 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12799
12800 return S_OK;
12801}
12802
12803/**
12804 * @note Locks this object for writing.
12805 */
12806STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12807{
12808 AutoCaller autoCaller(this);
12809 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12810
12811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12812
12813 mRemoveSavedState = aRemove;
12814
12815 return S_OK;
12816}
12817
12818/**
12819 * @note Locks the same as #setMachineState() does.
12820 */
12821STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12822{
12823 return setMachineState(aMachineState);
12824}
12825
12826/**
12827 * @note Locks this object for reading.
12828 */
12829STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12830{
12831 AutoCaller autoCaller(this);
12832 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12833
12834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12835
12836#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12837 mIPCSemName.cloneTo(aId);
12838 return S_OK;
12839#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12840# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12841 mIPCKey.cloneTo(aId);
12842# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12843 mData->m_strConfigFileFull.cloneTo(aId);
12844# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12845 return S_OK;
12846#else
12847# error "Port me!"
12848#endif
12849}
12850
12851/**
12852 * @note Locks this object for writing.
12853 */
12854STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12855{
12856 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12857 AutoCaller autoCaller(this);
12858 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12859
12860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12861
12862 if (mData->mSession.mState != SessionState_Locked)
12863 return VBOX_E_INVALID_OBJECT_STATE;
12864
12865 if (!mData->mSession.mProgress.isNull())
12866 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12867
12868 LogFlowThisFunc(("returns S_OK.\n"));
12869 return S_OK;
12870}
12871
12872/**
12873 * @note Locks this object for writing.
12874 */
12875STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12876{
12877 AutoCaller autoCaller(this);
12878 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12879
12880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12881
12882 if (mData->mSession.mState != SessionState_Locked)
12883 return VBOX_E_INVALID_OBJECT_STATE;
12884
12885 /* Finalize the LaunchVMProcess progress object. */
12886 if (mData->mSession.mProgress)
12887 {
12888 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12889 mData->mSession.mProgress.setNull();
12890 }
12891
12892 if (SUCCEEDED((HRESULT)iResult))
12893 {
12894#ifdef VBOX_WITH_RESOURCE_USAGE_API
12895 /* The VM has been powered up successfully, so it makes sense
12896 * now to offer the performance metrics for a running machine
12897 * object. Doing it earlier wouldn't be safe. */
12898 registerMetrics(mParent->performanceCollector(), mPeer,
12899 mData->mSession.mPID);
12900#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12901 }
12902
12903 return S_OK;
12904}
12905
12906/**
12907 * @note Locks this object for writing.
12908 */
12909STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12910{
12911 LogFlowThisFuncEnter();
12912
12913 CheckComArgOutPointerValid(aProgress);
12914
12915 AutoCaller autoCaller(this);
12916 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12917
12918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12919
12920 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12921 E_FAIL);
12922
12923 /* create a progress object to track operation completion */
12924 ComObjPtr<Progress> pProgress;
12925 pProgress.createObject();
12926 pProgress->init(getVirtualBox(),
12927 static_cast<IMachine *>(this) /* aInitiator */,
12928 Bstr(tr("Stopping the virtual machine")).raw(),
12929 FALSE /* aCancelable */);
12930
12931 /* fill in the console task data */
12932 mConsoleTaskData.mLastState = mData->mMachineState;
12933 mConsoleTaskData.mProgress = pProgress;
12934
12935 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12936 setMachineState(MachineState_Stopping);
12937
12938 pProgress.queryInterfaceTo(aProgress);
12939
12940 return S_OK;
12941}
12942
12943/**
12944 * @note Locks this object for writing.
12945 */
12946STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12947{
12948 LogFlowThisFuncEnter();
12949
12950 AutoCaller autoCaller(this);
12951 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12952
12953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12954
12955 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12956 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12957 && mConsoleTaskData.mLastState != MachineState_Null,
12958 E_FAIL);
12959
12960 /*
12961 * On failure, set the state to the state we had when BeginPoweringDown()
12962 * was called (this is expected by Console::PowerDown() and the associated
12963 * task). On success the VM process already changed the state to
12964 * MachineState_PoweredOff, so no need to do anything.
12965 */
12966 if (FAILED(iResult))
12967 setMachineState(mConsoleTaskData.mLastState);
12968
12969 /* notify the progress object about operation completion */
12970 Assert(mConsoleTaskData.mProgress);
12971 if (SUCCEEDED(iResult))
12972 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12973 else
12974 {
12975 Utf8Str strErrMsg(aErrMsg);
12976 if (strErrMsg.length())
12977 mConsoleTaskData.mProgress->notifyComplete(iResult,
12978 COM_IIDOF(ISession),
12979 getComponentName(),
12980 strErrMsg.c_str());
12981 else
12982 mConsoleTaskData.mProgress->notifyComplete(iResult);
12983 }
12984
12985 /* clear out the temporary saved state data */
12986 mConsoleTaskData.mLastState = MachineState_Null;
12987 mConsoleTaskData.mProgress.setNull();
12988
12989 LogFlowThisFuncLeave();
12990 return S_OK;
12991}
12992
12993
12994/**
12995 * Goes through the USB filters of the given machine to see if the given
12996 * device matches any filter or not.
12997 *
12998 * @note Locks the same as USBController::hasMatchingFilter() does.
12999 */
13000STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13001 BOOL *aMatched,
13002 ULONG *aMaskedIfs)
13003{
13004 LogFlowThisFunc(("\n"));
13005
13006 CheckComArgNotNull(aUSBDevice);
13007 CheckComArgOutPointerValid(aMatched);
13008
13009 AutoCaller autoCaller(this);
13010 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13011
13012#ifdef VBOX_WITH_USB
13013 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13014#else
13015 NOREF(aUSBDevice);
13016 NOREF(aMaskedIfs);
13017 *aMatched = FALSE;
13018#endif
13019
13020 return S_OK;
13021}
13022
13023/**
13024 * @note Locks the same as Host::captureUSBDevice() does.
13025 */
13026STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13027{
13028 LogFlowThisFunc(("\n"));
13029
13030 AutoCaller autoCaller(this);
13031 AssertComRCReturnRC(autoCaller.rc());
13032
13033#ifdef VBOX_WITH_USB
13034 /* if captureDeviceForVM() fails, it must have set extended error info */
13035 clearError();
13036 MultiResult rc = mParent->host()->checkUSBProxyService();
13037 if (FAILED(rc)) return rc;
13038
13039 USBProxyService *service = mParent->host()->usbProxyService();
13040 AssertReturn(service, E_FAIL);
13041 return service->captureDeviceForVM(this, Guid(aId).ref());
13042#else
13043 NOREF(aId);
13044 return E_NOTIMPL;
13045#endif
13046}
13047
13048/**
13049 * @note Locks the same as Host::detachUSBDevice() does.
13050 */
13051STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13052{
13053 LogFlowThisFunc(("\n"));
13054
13055 AutoCaller autoCaller(this);
13056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13057
13058#ifdef VBOX_WITH_USB
13059 USBProxyService *service = mParent->host()->usbProxyService();
13060 AssertReturn(service, E_FAIL);
13061 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13062#else
13063 NOREF(aId);
13064 NOREF(aDone);
13065 return E_NOTIMPL;
13066#endif
13067}
13068
13069/**
13070 * Inserts all machine filters to the USB proxy service and then calls
13071 * Host::autoCaptureUSBDevices().
13072 *
13073 * Called by Console from the VM process upon VM startup.
13074 *
13075 * @note Locks what called methods lock.
13076 */
13077STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13078{
13079 LogFlowThisFunc(("\n"));
13080
13081 AutoCaller autoCaller(this);
13082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13083
13084#ifdef VBOX_WITH_USB
13085 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
13086 AssertComRC(rc);
13087 NOREF(rc);
13088
13089 USBProxyService *service = mParent->host()->usbProxyService();
13090 AssertReturn(service, E_FAIL);
13091 return service->autoCaptureDevicesForVM(this);
13092#else
13093 return S_OK;
13094#endif
13095}
13096
13097/**
13098 * Removes all machine filters from the USB proxy service and then calls
13099 * Host::detachAllUSBDevices().
13100 *
13101 * Called by Console from the VM process upon normal VM termination or by
13102 * SessionMachine::uninit() upon abnormal VM termination (from under the
13103 * Machine/SessionMachine lock).
13104 *
13105 * @note Locks what called methods lock.
13106 */
13107STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13108{
13109 LogFlowThisFunc(("\n"));
13110
13111 AutoCaller autoCaller(this);
13112 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13113
13114#ifdef VBOX_WITH_USB
13115 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
13116 AssertComRC(rc);
13117 NOREF(rc);
13118
13119 USBProxyService *service = mParent->host()->usbProxyService();
13120 AssertReturn(service, E_FAIL);
13121 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13122#else
13123 NOREF(aDone);
13124 return S_OK;
13125#endif
13126}
13127
13128/**
13129 * @note Locks this object for writing.
13130 */
13131STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13132 IProgress **aProgress)
13133{
13134 LogFlowThisFuncEnter();
13135
13136 AssertReturn(aSession, E_INVALIDARG);
13137 AssertReturn(aProgress, E_INVALIDARG);
13138
13139 AutoCaller autoCaller(this);
13140
13141 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13142 /*
13143 * We don't assert below because it might happen that a non-direct session
13144 * informs us it is closed right after we've been uninitialized -- it's ok.
13145 */
13146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13147
13148 /* get IInternalSessionControl interface */
13149 ComPtr<IInternalSessionControl> control(aSession);
13150
13151 ComAssertRet(!control.isNull(), E_INVALIDARG);
13152
13153 /* Creating a Progress object requires the VirtualBox lock, and
13154 * thus locking it here is required by the lock order rules. */
13155 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13156
13157 if (control == mData->mSession.mDirectControl)
13158 {
13159 ComAssertRet(aProgress, E_POINTER);
13160
13161 /* The direct session is being normally closed by the client process
13162 * ----------------------------------------------------------------- */
13163
13164 /* go to the closing state (essential for all open*Session() calls and
13165 * for #checkForDeath()) */
13166 Assert(mData->mSession.mState == SessionState_Locked);
13167 mData->mSession.mState = SessionState_Unlocking;
13168
13169 /* set direct control to NULL to release the remote instance */
13170 mData->mSession.mDirectControl.setNull();
13171 LogFlowThisFunc(("Direct control is set to NULL\n"));
13172
13173 if (mData->mSession.mProgress)
13174 {
13175 /* finalize the progress, someone might wait if a frontend
13176 * closes the session before powering on the VM. */
13177 mData->mSession.mProgress->notifyComplete(E_FAIL,
13178 COM_IIDOF(ISession),
13179 getComponentName(),
13180 tr("The VM session was closed before any attempt to power it on"));
13181 mData->mSession.mProgress.setNull();
13182 }
13183
13184 /* Create the progress object the client will use to wait until
13185 * #checkForDeath() is called to uninitialize this session object after
13186 * it releases the IPC semaphore.
13187 * Note! Because we're "reusing" mProgress here, this must be a proxy
13188 * object just like for LaunchVMProcess. */
13189 Assert(mData->mSession.mProgress.isNull());
13190 ComObjPtr<ProgressProxy> progress;
13191 progress.createObject();
13192 ComPtr<IUnknown> pPeer(mPeer);
13193 progress->init(mParent, pPeer,
13194 Bstr(tr("Closing session")).raw(),
13195 FALSE /* aCancelable */);
13196 progress.queryInterfaceTo(aProgress);
13197 mData->mSession.mProgress = progress;
13198 }
13199 else
13200 {
13201 /* the remote session is being normally closed */
13202 Data::Session::RemoteControlList::iterator it =
13203 mData->mSession.mRemoteControls.begin();
13204 while (it != mData->mSession.mRemoteControls.end())
13205 {
13206 if (control == *it)
13207 break;
13208 ++it;
13209 }
13210 BOOL found = it != mData->mSession.mRemoteControls.end();
13211 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13212 E_INVALIDARG);
13213 // This MUST be erase(it), not remove(*it) as the latter triggers a
13214 // very nasty use after free due to the place where the value "lives".
13215 mData->mSession.mRemoteControls.erase(it);
13216 }
13217
13218 /* signal the client watcher thread, because the client is going away */
13219 mParent->updateClientWatcher();
13220
13221 LogFlowThisFuncLeave();
13222 return S_OK;
13223}
13224
13225/**
13226 * @note Locks this object for writing.
13227 */
13228STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13229{
13230 LogFlowThisFuncEnter();
13231
13232 CheckComArgOutPointerValid(aProgress);
13233 CheckComArgOutPointerValid(aStateFilePath);
13234
13235 AutoCaller autoCaller(this);
13236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13237
13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13239
13240 AssertReturn( mData->mMachineState == MachineState_Paused
13241 && mConsoleTaskData.mLastState == MachineState_Null
13242 && mConsoleTaskData.strStateFilePath.isEmpty(),
13243 E_FAIL);
13244
13245 /* create a progress object to track operation completion */
13246 ComObjPtr<Progress> pProgress;
13247 pProgress.createObject();
13248 pProgress->init(getVirtualBox(),
13249 static_cast<IMachine *>(this) /* aInitiator */,
13250 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13251 FALSE /* aCancelable */);
13252
13253 Utf8Str strStateFilePath;
13254 /* stateFilePath is null when the machine is not running */
13255 if (mData->mMachineState == MachineState_Paused)
13256 composeSavedStateFilename(strStateFilePath);
13257
13258 /* fill in the console task data */
13259 mConsoleTaskData.mLastState = mData->mMachineState;
13260 mConsoleTaskData.strStateFilePath = strStateFilePath;
13261 mConsoleTaskData.mProgress = pProgress;
13262
13263 /* set the state to Saving (this is expected by Console::SaveState()) */
13264 setMachineState(MachineState_Saving);
13265
13266 strStateFilePath.cloneTo(aStateFilePath);
13267 pProgress.queryInterfaceTo(aProgress);
13268
13269 return S_OK;
13270}
13271
13272/**
13273 * @note Locks mParent + this object for writing.
13274 */
13275STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13276{
13277 LogFlowThisFunc(("\n"));
13278
13279 AutoCaller autoCaller(this);
13280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13281
13282 /* endSavingState() need mParent lock */
13283 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13284
13285 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13286 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13287 && mConsoleTaskData.mLastState != MachineState_Null
13288 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13289 E_FAIL);
13290
13291 /*
13292 * On failure, set the state to the state we had when BeginSavingState()
13293 * was called (this is expected by Console::SaveState() and the associated
13294 * task). On success the VM process already changed the state to
13295 * MachineState_Saved, so no need to do anything.
13296 */
13297 if (FAILED(iResult))
13298 setMachineState(mConsoleTaskData.mLastState);
13299
13300 return endSavingState(iResult, aErrMsg);
13301}
13302
13303/**
13304 * @note Locks this object for writing.
13305 */
13306STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13307{
13308 LogFlowThisFunc(("\n"));
13309
13310 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13311
13312 AutoCaller autoCaller(this);
13313 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13314
13315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13316
13317 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13318 || mData->mMachineState == MachineState_Teleported
13319 || mData->mMachineState == MachineState_Aborted
13320 , E_FAIL); /** @todo setError. */
13321
13322 Utf8Str stateFilePathFull = aSavedStateFile;
13323 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13324 if (RT_FAILURE(vrc))
13325 return setError(VBOX_E_FILE_ERROR,
13326 tr("Invalid saved state file path '%ls' (%Rrc)"),
13327 aSavedStateFile,
13328 vrc);
13329
13330 mSSData->strStateFilePath = stateFilePathFull;
13331
13332 /* The below setMachineState() will detect the state transition and will
13333 * update the settings file */
13334
13335 return setMachineState(MachineState_Saved);
13336}
13337
13338STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13339 ComSafeArrayOut(BSTR, aValues),
13340 ComSafeArrayOut(LONG64, aTimestamps),
13341 ComSafeArrayOut(BSTR, aFlags))
13342{
13343 LogFlowThisFunc(("\n"));
13344
13345#ifdef VBOX_WITH_GUEST_PROPS
13346 using namespace guestProp;
13347
13348 AutoCaller autoCaller(this);
13349 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13350
13351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13352
13353 CheckComArgOutSafeArrayPointerValid(aNames);
13354 CheckComArgOutSafeArrayPointerValid(aValues);
13355 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13356 CheckComArgOutSafeArrayPointerValid(aFlags);
13357
13358 size_t cEntries = mHWData->mGuestProperties.size();
13359 com::SafeArray<BSTR> names(cEntries);
13360 com::SafeArray<BSTR> values(cEntries);
13361 com::SafeArray<LONG64> timestamps(cEntries);
13362 com::SafeArray<BSTR> flags(cEntries);
13363 unsigned i = 0;
13364 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13365 it != mHWData->mGuestProperties.end();
13366 ++it)
13367 {
13368 char szFlags[MAX_FLAGS_LEN + 1];
13369 it->first.cloneTo(&names[i]);
13370 it->second.strValue.cloneTo(&values[i]);
13371 timestamps[i] = it->second.mTimestamp;
13372 /* If it is NULL, keep it NULL. */
13373 if (it->second.mFlags)
13374 {
13375 writeFlags(it->second.mFlags, szFlags);
13376 Bstr(szFlags).cloneTo(&flags[i]);
13377 }
13378 else
13379 flags[i] = NULL;
13380 ++i;
13381 }
13382 names.detachTo(ComSafeArrayOutArg(aNames));
13383 values.detachTo(ComSafeArrayOutArg(aValues));
13384 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13385 flags.detachTo(ComSafeArrayOutArg(aFlags));
13386 return S_OK;
13387#else
13388 ReturnComNotImplemented();
13389#endif
13390}
13391
13392STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13393 IN_BSTR aValue,
13394 LONG64 aTimestamp,
13395 IN_BSTR aFlags)
13396{
13397 LogFlowThisFunc(("\n"));
13398
13399#ifdef VBOX_WITH_GUEST_PROPS
13400 using namespace guestProp;
13401
13402 CheckComArgStrNotEmptyOrNull(aName);
13403 CheckComArgNotNull(aValue);
13404 CheckComArgNotNull(aFlags);
13405
13406 try
13407 {
13408 /*
13409 * Convert input up front.
13410 */
13411 Utf8Str utf8Name(aName);
13412 uint32_t fFlags = NILFLAG;
13413 if (aFlags)
13414 {
13415 Utf8Str utf8Flags(aFlags);
13416 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13417 AssertRCReturn(vrc, E_INVALIDARG);
13418 }
13419
13420 /*
13421 * Now grab the object lock, validate the state and do the update.
13422 */
13423 AutoCaller autoCaller(this);
13424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13425
13426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13427
13428 switch (mData->mMachineState)
13429 {
13430 case MachineState_Paused:
13431 case MachineState_Running:
13432 case MachineState_Teleporting:
13433 case MachineState_TeleportingPausedVM:
13434 case MachineState_LiveSnapshotting:
13435 case MachineState_DeletingSnapshotOnline:
13436 case MachineState_DeletingSnapshotPaused:
13437 case MachineState_Saving:
13438 case MachineState_Stopping:
13439 break;
13440
13441 default:
13442 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13443 VBOX_E_INVALID_VM_STATE);
13444 }
13445
13446 setModified(IsModified_MachineData);
13447 mHWData.backup();
13448
13449 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13450 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13451 if (it != mHWData->mGuestProperties.end())
13452 {
13453 if (!fDelete)
13454 {
13455 it->second.strValue = aValue;
13456 it->second.mTimestamp = aTimestamp;
13457 it->second.mFlags = fFlags;
13458 }
13459 else
13460 mHWData->mGuestProperties.erase(it);
13461
13462 mData->mGuestPropertiesModified = TRUE;
13463 }
13464 else if (!fDelete)
13465 {
13466 HWData::GuestProperty prop;
13467 prop.strValue = aValue;
13468 prop.mTimestamp = aTimestamp;
13469 prop.mFlags = fFlags;
13470
13471 mHWData->mGuestProperties[utf8Name] = prop;
13472 mData->mGuestPropertiesModified = TRUE;
13473 }
13474
13475 /*
13476 * Send a callback notification if appropriate
13477 */
13478 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13479 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13480 RTSTR_MAX,
13481 utf8Name.c_str(),
13482 RTSTR_MAX, NULL)
13483 )
13484 {
13485 alock.release();
13486
13487 mParent->onGuestPropertyChange(mData->mUuid,
13488 aName,
13489 aValue,
13490 aFlags);
13491 }
13492 }
13493 catch (...)
13494 {
13495 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13496 }
13497 return S_OK;
13498#else
13499 ReturnComNotImplemented();
13500#endif
13501}
13502
13503STDMETHODIMP SessionMachine::LockMedia()
13504{
13505 AutoCaller autoCaller(this);
13506 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13507
13508 AutoMultiWriteLock2 alock(this->lockHandle(),
13509 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13510
13511 AssertReturn( mData->mMachineState == MachineState_Starting
13512 || mData->mMachineState == MachineState_Restoring
13513 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13514
13515 clearError();
13516 alock.release();
13517 return lockMedia();
13518}
13519
13520STDMETHODIMP SessionMachine::UnlockMedia()
13521{
13522 unlockMedia();
13523 return S_OK;
13524}
13525
13526STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13527 IMediumAttachment **aNewAttachment)
13528{
13529 CheckComArgNotNull(aAttachment);
13530 CheckComArgOutPointerValid(aNewAttachment);
13531
13532 AutoCaller autoCaller(this);
13533 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13534
13535 // request the host lock first, since might be calling Host methods for getting host drives;
13536 // next, protect the media tree all the while we're in here, as well as our member variables
13537 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13538 this->lockHandle(),
13539 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13540
13541 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13542
13543 Bstr ctrlName;
13544 LONG lPort;
13545 LONG lDevice;
13546 bool fTempEject;
13547 {
13548 AutoCaller autoAttachCaller(this);
13549 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13550
13551 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13552
13553 /* Need to query the details first, as the IMediumAttachment reference
13554 * might be to the original settings, which we are going to change. */
13555 ctrlName = pAttach->getControllerName();
13556 lPort = pAttach->getPort();
13557 lDevice = pAttach->getDevice();
13558 fTempEject = pAttach->getTempEject();
13559 }
13560
13561 if (!fTempEject)
13562 {
13563 /* Remember previously mounted medium. The medium before taking the
13564 * backup is not necessarily the same thing. */
13565 ComObjPtr<Medium> oldmedium;
13566 oldmedium = pAttach->getMedium();
13567
13568 setModified(IsModified_Storage);
13569 mMediaData.backup();
13570
13571 // The backup operation makes the pAttach reference point to the
13572 // old settings. Re-get the correct reference.
13573 pAttach = findAttachment(mMediaData->mAttachments,
13574 ctrlName.raw(),
13575 lPort,
13576 lDevice);
13577
13578 {
13579 AutoCaller autoAttachCaller(this);
13580 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13581
13582 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13583 if (!oldmedium.isNull())
13584 oldmedium->removeBackReference(mData->mUuid);
13585
13586 pAttach->updateMedium(NULL);
13587 pAttach->updateEjected();
13588 }
13589
13590 setModified(IsModified_Storage);
13591 }
13592 else
13593 {
13594 {
13595 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13596 pAttach->updateEjected();
13597 }
13598 }
13599
13600 pAttach.queryInterfaceTo(aNewAttachment);
13601
13602 return S_OK;
13603}
13604
13605// public methods only for internal purposes
13606/////////////////////////////////////////////////////////////////////////////
13607
13608/**
13609 * Called from the client watcher thread to check for expected or unexpected
13610 * death of the client process that has a direct session to this machine.
13611 *
13612 * On Win32 and on OS/2, this method is called only when we've got the
13613 * mutex (i.e. the client has either died or terminated normally) so it always
13614 * returns @c true (the client is terminated, the session machine is
13615 * uninitialized).
13616 *
13617 * On other platforms, the method returns @c true if the client process has
13618 * terminated normally or abnormally and the session machine was uninitialized,
13619 * and @c false if the client process is still alive.
13620 *
13621 * @note Locks this object for writing.
13622 */
13623bool SessionMachine::checkForDeath()
13624{
13625 Uninit::Reason reason;
13626 bool terminated = false;
13627
13628 /* Enclose autoCaller with a block because calling uninit() from under it
13629 * will deadlock. */
13630 {
13631 AutoCaller autoCaller(this);
13632 if (!autoCaller.isOk())
13633 {
13634 /* return true if not ready, to cause the client watcher to exclude
13635 * the corresponding session from watching */
13636 LogFlowThisFunc(("Already uninitialized!\n"));
13637 return true;
13638 }
13639
13640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13641
13642 /* Determine the reason of death: if the session state is Closing here,
13643 * everything is fine. Otherwise it means that the client did not call
13644 * OnSessionEnd() before it released the IPC semaphore. This may happen
13645 * either because the client process has abnormally terminated, or
13646 * because it simply forgot to call ISession::Close() before exiting. We
13647 * threat the latter also as an abnormal termination (see
13648 * Session::uninit() for details). */
13649 reason = mData->mSession.mState == SessionState_Unlocking ?
13650 Uninit::Normal :
13651 Uninit::Abnormal;
13652
13653#if defined(RT_OS_WINDOWS)
13654
13655 AssertMsg(mIPCSem, ("semaphore must be created"));
13656
13657 /* release the IPC mutex */
13658 ::ReleaseMutex(mIPCSem);
13659
13660 terminated = true;
13661
13662#elif defined(RT_OS_OS2)
13663
13664 AssertMsg(mIPCSem, ("semaphore must be created"));
13665
13666 /* release the IPC mutex */
13667 ::DosReleaseMutexSem(mIPCSem);
13668
13669 terminated = true;
13670
13671#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13672
13673 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13674
13675 int val = ::semctl(mIPCSem, 0, GETVAL);
13676 if (val > 0)
13677 {
13678 /* the semaphore is signaled, meaning the session is terminated */
13679 terminated = true;
13680 }
13681
13682#else
13683# error "Port me!"
13684#endif
13685
13686 } /* AutoCaller block */
13687
13688 if (terminated)
13689 uninit(reason);
13690
13691 return terminated;
13692}
13693
13694/**
13695 * @note Locks this object for reading.
13696 */
13697HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13698{
13699 LogFlowThisFunc(("\n"));
13700
13701 AutoCaller autoCaller(this);
13702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13703
13704 ComPtr<IInternalSessionControl> directControl;
13705 {
13706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13707 directControl = mData->mSession.mDirectControl;
13708 }
13709
13710 /* ignore notifications sent after #OnSessionEnd() is called */
13711 if (!directControl)
13712 return S_OK;
13713
13714 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13715}
13716
13717/**
13718 * @note Locks this object for reading.
13719 */
13720HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13721 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13722{
13723 LogFlowThisFunc(("\n"));
13724
13725 AutoCaller autoCaller(this);
13726 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13727
13728 ComPtr<IInternalSessionControl> directControl;
13729 {
13730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13731 directControl = mData->mSession.mDirectControl;
13732 }
13733
13734 /* ignore notifications sent after #OnSessionEnd() is called */
13735 if (!directControl)
13736 return S_OK;
13737 /*
13738 * instead acting like callback we ask IVirtualBox deliver corresponding event
13739 */
13740
13741 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13742 return S_OK;
13743}
13744
13745/**
13746 * @note Locks this object for reading.
13747 */
13748HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13749{
13750 LogFlowThisFunc(("\n"));
13751
13752 AutoCaller autoCaller(this);
13753 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13754
13755 ComPtr<IInternalSessionControl> directControl;
13756 {
13757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13758 directControl = mData->mSession.mDirectControl;
13759 }
13760
13761 /* ignore notifications sent after #OnSessionEnd() is called */
13762 if (!directControl)
13763 return S_OK;
13764
13765 return directControl->OnSerialPortChange(serialPort);
13766}
13767
13768/**
13769 * @note Locks this object for reading.
13770 */
13771HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13772{
13773 LogFlowThisFunc(("\n"));
13774
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13777
13778 ComPtr<IInternalSessionControl> directControl;
13779 {
13780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13781 directControl = mData->mSession.mDirectControl;
13782 }
13783
13784 /* ignore notifications sent after #OnSessionEnd() is called */
13785 if (!directControl)
13786 return S_OK;
13787
13788 return directControl->OnParallelPortChange(parallelPort);
13789}
13790
13791/**
13792 * @note Locks this object for reading.
13793 */
13794HRESULT SessionMachine::onStorageControllerChange()
13795{
13796 LogFlowThisFunc(("\n"));
13797
13798 AutoCaller autoCaller(this);
13799 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13800
13801 ComPtr<IInternalSessionControl> directControl;
13802 {
13803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13804 directControl = mData->mSession.mDirectControl;
13805 }
13806
13807 /* ignore notifications sent after #OnSessionEnd() is called */
13808 if (!directControl)
13809 return S_OK;
13810
13811 return directControl->OnStorageControllerChange();
13812}
13813
13814/**
13815 * @note Locks this object for reading.
13816 */
13817HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13818{
13819 LogFlowThisFunc(("\n"));
13820
13821 AutoCaller autoCaller(this);
13822 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13823
13824 ComPtr<IInternalSessionControl> directControl;
13825 {
13826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13827 directControl = mData->mSession.mDirectControl;
13828 }
13829
13830 /* ignore notifications sent after #OnSessionEnd() is called */
13831 if (!directControl)
13832 return S_OK;
13833
13834 return directControl->OnMediumChange(aAttachment, aForce);
13835}
13836
13837/**
13838 * @note Locks this object for reading.
13839 */
13840HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13841{
13842 LogFlowThisFunc(("\n"));
13843
13844 AutoCaller autoCaller(this);
13845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13846
13847 ComPtr<IInternalSessionControl> directControl;
13848 {
13849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13850 directControl = mData->mSession.mDirectControl;
13851 }
13852
13853 /* ignore notifications sent after #OnSessionEnd() is called */
13854 if (!directControl)
13855 return S_OK;
13856
13857 return directControl->OnCPUChange(aCPU, aRemove);
13858}
13859
13860HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13861{
13862 LogFlowThisFunc(("\n"));
13863
13864 AutoCaller autoCaller(this);
13865 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13866
13867 ComPtr<IInternalSessionControl> directControl;
13868 {
13869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13870 directControl = mData->mSession.mDirectControl;
13871 }
13872
13873 /* ignore notifications sent after #OnSessionEnd() is called */
13874 if (!directControl)
13875 return S_OK;
13876
13877 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13878}
13879
13880/**
13881 * @note Locks this object for reading.
13882 */
13883HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13884{
13885 LogFlowThisFunc(("\n"));
13886
13887 AutoCaller autoCaller(this);
13888 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13889
13890 ComPtr<IInternalSessionControl> directControl;
13891 {
13892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13893 directControl = mData->mSession.mDirectControl;
13894 }
13895
13896 /* ignore notifications sent after #OnSessionEnd() is called */
13897 if (!directControl)
13898 return S_OK;
13899
13900 return directControl->OnVRDEServerChange(aRestart);
13901}
13902
13903/**
13904 * @note Locks this object for reading.
13905 */
13906HRESULT SessionMachine::onVideoCaptureChange()
13907{
13908 LogFlowThisFunc(("\n"));
13909
13910 AutoCaller autoCaller(this);
13911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13912
13913 ComPtr<IInternalSessionControl> directControl;
13914 {
13915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13916 directControl = mData->mSession.mDirectControl;
13917 }
13918
13919 /* ignore notifications sent after #OnSessionEnd() is called */
13920 if (!directControl)
13921 return S_OK;
13922
13923 return directControl->OnVideoCaptureChange();
13924}
13925
13926/**
13927 * @note Locks this object for reading.
13928 */
13929HRESULT SessionMachine::onUSBControllerChange()
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13935
13936 ComPtr<IInternalSessionControl> directControl;
13937 {
13938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13939 directControl = mData->mSession.mDirectControl;
13940 }
13941
13942 /* ignore notifications sent after #OnSessionEnd() is called */
13943 if (!directControl)
13944 return S_OK;
13945
13946 return directControl->OnUSBControllerChange();
13947}
13948
13949/**
13950 * @note Locks this object for reading.
13951 */
13952HRESULT SessionMachine::onSharedFolderChange()
13953{
13954 LogFlowThisFunc(("\n"));
13955
13956 AutoCaller autoCaller(this);
13957 AssertComRCReturnRC(autoCaller.rc());
13958
13959 ComPtr<IInternalSessionControl> directControl;
13960 {
13961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13962 directControl = mData->mSession.mDirectControl;
13963 }
13964
13965 /* ignore notifications sent after #OnSessionEnd() is called */
13966 if (!directControl)
13967 return S_OK;
13968
13969 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13970}
13971
13972/**
13973 * @note Locks this object for reading.
13974 */
13975HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13976{
13977 LogFlowThisFunc(("\n"));
13978
13979 AutoCaller autoCaller(this);
13980 AssertComRCReturnRC(autoCaller.rc());
13981
13982 ComPtr<IInternalSessionControl> directControl;
13983 {
13984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13985 directControl = mData->mSession.mDirectControl;
13986 }
13987
13988 /* ignore notifications sent after #OnSessionEnd() is called */
13989 if (!directControl)
13990 return S_OK;
13991
13992 return directControl->OnClipboardModeChange(aClipboardMode);
13993}
13994
13995/**
13996 * @note Locks this object for reading.
13997 */
13998HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13999{
14000 LogFlowThisFunc(("\n"));
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturnRC(autoCaller.rc());
14004
14005 ComPtr<IInternalSessionControl> directControl;
14006 {
14007 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14008 directControl = mData->mSession.mDirectControl;
14009 }
14010
14011 /* ignore notifications sent after #OnSessionEnd() is called */
14012 if (!directControl)
14013 return S_OK;
14014
14015 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14016}
14017
14018/**
14019 * @note Locks this object for reading.
14020 */
14021HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14022{
14023 LogFlowThisFunc(("\n"));
14024
14025 AutoCaller autoCaller(this);
14026 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14027
14028 ComPtr<IInternalSessionControl> directControl;
14029 {
14030 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14031 directControl = mData->mSession.mDirectControl;
14032 }
14033
14034 /* ignore notifications sent after #OnSessionEnd() is called */
14035 if (!directControl)
14036 return S_OK;
14037
14038 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14039}
14040
14041/**
14042 * @note Locks this object for reading.
14043 */
14044HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14045{
14046 LogFlowThisFunc(("\n"));
14047
14048 AutoCaller autoCaller(this);
14049 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14050
14051 ComPtr<IInternalSessionControl> directControl;
14052 {
14053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14054 directControl = mData->mSession.mDirectControl;
14055 }
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14062}
14063
14064/**
14065 * Returns @c true if this machine's USB controller reports it has a matching
14066 * filter for the given USB device and @c false otherwise.
14067 *
14068 * @note locks this object for reading.
14069 */
14070bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14071{
14072 AutoCaller autoCaller(this);
14073 /* silently return if not ready -- this method may be called after the
14074 * direct machine session has been called */
14075 if (!autoCaller.isOk())
14076 return false;
14077
14078#ifdef VBOX_WITH_USB
14079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14080
14081 switch (mData->mMachineState)
14082 {
14083 case MachineState_Starting:
14084 case MachineState_Restoring:
14085 case MachineState_TeleportingIn:
14086 case MachineState_Paused:
14087 case MachineState_Running:
14088 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14089 * elsewhere... */
14090 alock.release();
14091 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
14092 default: break;
14093 }
14094#else
14095 NOREF(aDevice);
14096 NOREF(aMaskedIfs);
14097#endif
14098 return false;
14099}
14100
14101/**
14102 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14103 */
14104HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14105 IVirtualBoxErrorInfo *aError,
14106 ULONG aMaskedIfs)
14107{
14108 LogFlowThisFunc(("\n"));
14109
14110 AutoCaller autoCaller(this);
14111
14112 /* This notification may happen after the machine object has been
14113 * uninitialized (the session was closed), so don't assert. */
14114 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14115
14116 ComPtr<IInternalSessionControl> directControl;
14117 {
14118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14119 directControl = mData->mSession.mDirectControl;
14120 }
14121
14122 /* fail on notifications sent after #OnSessionEnd() is called, it is
14123 * expected by the caller */
14124 if (!directControl)
14125 return E_FAIL;
14126
14127 /* No locks should be held at this point. */
14128 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14129 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14130
14131 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14132}
14133
14134/**
14135 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14136 */
14137HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14138 IVirtualBoxErrorInfo *aError)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143
14144 /* This notification may happen after the machine object has been
14145 * uninitialized (the session was closed), so don't assert. */
14146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 directControl = mData->mSession.mDirectControl;
14152 }
14153
14154 /* fail on notifications sent after #OnSessionEnd() is called, it is
14155 * expected by the caller */
14156 if (!directControl)
14157 return E_FAIL;
14158
14159 /* No locks should be held at this point. */
14160 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14161 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14162
14163 return directControl->OnUSBDeviceDetach(aId, aError);
14164}
14165
14166// protected methods
14167/////////////////////////////////////////////////////////////////////////////
14168
14169/**
14170 * Helper method to finalize saving the state.
14171 *
14172 * @note Must be called from under this object's lock.
14173 *
14174 * @param aRc S_OK if the snapshot has been taken successfully
14175 * @param aErrMsg human readable error message for failure
14176 *
14177 * @note Locks mParent + this objects for writing.
14178 */
14179HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14180{
14181 LogFlowThisFuncEnter();
14182
14183 AutoCaller autoCaller(this);
14184 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14185
14186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14187
14188 HRESULT rc = S_OK;
14189
14190 if (SUCCEEDED(aRc))
14191 {
14192 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14193
14194 /* save all VM settings */
14195 rc = saveSettings(NULL);
14196 // no need to check whether VirtualBox.xml needs saving also since
14197 // we can't have a name change pending at this point
14198 }
14199 else
14200 {
14201 // delete the saved state file (it might have been already created);
14202 // we need not check whether this is shared with a snapshot here because
14203 // we certainly created this saved state file here anew
14204 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14205 }
14206
14207 /* notify the progress object about operation completion */
14208 Assert(mConsoleTaskData.mProgress);
14209 if (SUCCEEDED(aRc))
14210 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14211 else
14212 {
14213 if (aErrMsg.length())
14214 mConsoleTaskData.mProgress->notifyComplete(aRc,
14215 COM_IIDOF(ISession),
14216 getComponentName(),
14217 aErrMsg.c_str());
14218 else
14219 mConsoleTaskData.mProgress->notifyComplete(aRc);
14220 }
14221
14222 /* clear out the temporary saved state data */
14223 mConsoleTaskData.mLastState = MachineState_Null;
14224 mConsoleTaskData.strStateFilePath.setNull();
14225 mConsoleTaskData.mProgress.setNull();
14226
14227 LogFlowThisFuncLeave();
14228 return rc;
14229}
14230
14231/**
14232 * Deletes the given file if it is no longer in use by either the current machine state
14233 * (if the machine is "saved") or any of the machine's snapshots.
14234 *
14235 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14236 * but is different for each SnapshotMachine. When calling this, the order of calling this
14237 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14238 * is therefore critical. I know, it's all rather messy.
14239 *
14240 * @param strStateFile
14241 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14242 */
14243void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14244 Snapshot *pSnapshotToIgnore)
14245{
14246 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14247 if ( (strStateFile.isNotEmpty())
14248 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14249 )
14250 // ... and it must also not be shared with other snapshots
14251 if ( !mData->mFirstSnapshot
14252 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14253 // this checks the SnapshotMachine's state file paths
14254 )
14255 RTFileDelete(strStateFile.c_str());
14256}
14257
14258/**
14259 * Locks the attached media.
14260 *
14261 * All attached hard disks are locked for writing and DVD/floppy are locked for
14262 * reading. Parents of attached hard disks (if any) are locked for reading.
14263 *
14264 * This method also performs accessibility check of all media it locks: if some
14265 * media is inaccessible, the method will return a failure and a bunch of
14266 * extended error info objects per each inaccessible medium.
14267 *
14268 * Note that this method is atomic: if it returns a success, all media are
14269 * locked as described above; on failure no media is locked at all (all
14270 * succeeded individual locks will be undone).
14271 *
14272 * The caller is responsible for doing the necessary state sanity checks.
14273 *
14274 * The locks made by this method must be undone by calling #unlockMedia() when
14275 * no more needed.
14276 */
14277HRESULT SessionMachine::lockMedia()
14278{
14279 AutoCaller autoCaller(this);
14280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14281
14282 AutoMultiWriteLock2 alock(this->lockHandle(),
14283 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14284
14285 /* bail out if trying to lock things with already set up locking */
14286 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14287
14288 MultiResult mrc(S_OK);
14289
14290 /* Collect locking information for all medium objects attached to the VM. */
14291 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14292 it != mMediaData->mAttachments.end();
14293 ++it)
14294 {
14295 MediumAttachment* pAtt = *it;
14296 DeviceType_T devType = pAtt->getType();
14297 Medium *pMedium = pAtt->getMedium();
14298
14299 MediumLockList *pMediumLockList(new MediumLockList());
14300 // There can be attachments without a medium (floppy/dvd), and thus
14301 // it's impossible to create a medium lock list. It still makes sense
14302 // to have the empty medium lock list in the map in case a medium is
14303 // attached later.
14304 if (pMedium != NULL)
14305 {
14306 MediumType_T mediumType = pMedium->getType();
14307 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14308 || mediumType == MediumType_Shareable;
14309 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14310
14311 alock.release();
14312 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14313 !fIsReadOnlyLock /* fMediumLockWrite */,
14314 NULL,
14315 *pMediumLockList);
14316 alock.acquire();
14317 if (FAILED(mrc))
14318 {
14319 delete pMediumLockList;
14320 mData->mSession.mLockedMedia.Clear();
14321 break;
14322 }
14323 }
14324
14325 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14326 if (FAILED(rc))
14327 {
14328 mData->mSession.mLockedMedia.Clear();
14329 mrc = setError(rc,
14330 tr("Collecting locking information for all attached media failed"));
14331 break;
14332 }
14333 }
14334
14335 if (SUCCEEDED(mrc))
14336 {
14337 /* Now lock all media. If this fails, nothing is locked. */
14338 alock.release();
14339 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14340 alock.acquire();
14341 if (FAILED(rc))
14342 {
14343 mrc = setError(rc,
14344 tr("Locking of attached media failed"));
14345 }
14346 }
14347
14348 return mrc;
14349}
14350
14351/**
14352 * Undoes the locks made by by #lockMedia().
14353 */
14354void SessionMachine::unlockMedia()
14355{
14356 AutoCaller autoCaller(this);
14357 AssertComRCReturnVoid(autoCaller.rc());
14358
14359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14360
14361 /* we may be holding important error info on the current thread;
14362 * preserve it */
14363 ErrorInfoKeeper eik;
14364
14365 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14366 AssertComRC(rc);
14367}
14368
14369/**
14370 * Helper to change the machine state (reimplementation).
14371 *
14372 * @note Locks this object for writing.
14373 * @note This method must not call saveSettings or SaveSettings, otherwise
14374 * it can cause crashes in random places due to unexpectedly committing
14375 * the current settings. The caller is responsible for that. The call
14376 * to saveStateSettings is fine, because this method does not commit.
14377 */
14378HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14379{
14380 LogFlowThisFuncEnter();
14381 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14382
14383 AutoCaller autoCaller(this);
14384 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14385
14386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14387
14388 MachineState_T oldMachineState = mData->mMachineState;
14389
14390 AssertMsgReturn(oldMachineState != aMachineState,
14391 ("oldMachineState=%s, aMachineState=%s\n",
14392 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14393 E_FAIL);
14394
14395 HRESULT rc = S_OK;
14396
14397 int stsFlags = 0;
14398 bool deleteSavedState = false;
14399
14400 /* detect some state transitions */
14401
14402 if ( ( oldMachineState == MachineState_Saved
14403 && aMachineState == MachineState_Restoring)
14404 || ( ( oldMachineState == MachineState_PoweredOff
14405 || oldMachineState == MachineState_Teleported
14406 || oldMachineState == MachineState_Aborted
14407 )
14408 && ( aMachineState == MachineState_TeleportingIn
14409 || aMachineState == MachineState_Starting
14410 )
14411 )
14412 )
14413 {
14414 /* The EMT thread is about to start */
14415
14416 /* Nothing to do here for now... */
14417
14418 /// @todo NEWMEDIA don't let mDVDDrive and other children
14419 /// change anything when in the Starting/Restoring state
14420 }
14421 else if ( ( oldMachineState == MachineState_Running
14422 || oldMachineState == MachineState_Paused
14423 || oldMachineState == MachineState_Teleporting
14424 || oldMachineState == MachineState_LiveSnapshotting
14425 || oldMachineState == MachineState_Stuck
14426 || oldMachineState == MachineState_Starting
14427 || oldMachineState == MachineState_Stopping
14428 || oldMachineState == MachineState_Saving
14429 || oldMachineState == MachineState_Restoring
14430 || oldMachineState == MachineState_TeleportingPausedVM
14431 || oldMachineState == MachineState_TeleportingIn
14432 )
14433 && ( aMachineState == MachineState_PoweredOff
14434 || aMachineState == MachineState_Saved
14435 || aMachineState == MachineState_Teleported
14436 || aMachineState == MachineState_Aborted
14437 )
14438 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14439 * snapshot */
14440 && ( mConsoleTaskData.mSnapshot.isNull()
14441 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14442 )
14443 )
14444 {
14445 /* The EMT thread has just stopped, unlock attached media. Note that as
14446 * opposed to locking that is done from Console, we do unlocking here
14447 * because the VM process may have aborted before having a chance to
14448 * properly unlock all media it locked. */
14449
14450 unlockMedia();
14451 }
14452
14453 if (oldMachineState == MachineState_Restoring)
14454 {
14455 if (aMachineState != MachineState_Saved)
14456 {
14457 /*
14458 * delete the saved state file once the machine has finished
14459 * restoring from it (note that Console sets the state from
14460 * Restoring to Saved if the VM couldn't restore successfully,
14461 * to give the user an ability to fix an error and retry --
14462 * we keep the saved state file in this case)
14463 */
14464 deleteSavedState = true;
14465 }
14466 }
14467 else if ( oldMachineState == MachineState_Saved
14468 && ( aMachineState == MachineState_PoweredOff
14469 || aMachineState == MachineState_Aborted
14470 || aMachineState == MachineState_Teleported
14471 )
14472 )
14473 {
14474 /*
14475 * delete the saved state after Console::ForgetSavedState() is called
14476 * or if the VM process (owning a direct VM session) crashed while the
14477 * VM was Saved
14478 */
14479
14480 /// @todo (dmik)
14481 // Not sure that deleting the saved state file just because of the
14482 // client death before it attempted to restore the VM is a good
14483 // thing. But when it crashes we need to go to the Aborted state
14484 // which cannot have the saved state file associated... The only
14485 // way to fix this is to make the Aborted condition not a VM state
14486 // but a bool flag: i.e., when a crash occurs, set it to true and
14487 // change the state to PoweredOff or Saved depending on the
14488 // saved state presence.
14489
14490 deleteSavedState = true;
14491 mData->mCurrentStateModified = TRUE;
14492 stsFlags |= SaveSTS_CurStateModified;
14493 }
14494
14495 if ( aMachineState == MachineState_Starting
14496 || aMachineState == MachineState_Restoring
14497 || aMachineState == MachineState_TeleportingIn
14498 )
14499 {
14500 /* set the current state modified flag to indicate that the current
14501 * state is no more identical to the state in the
14502 * current snapshot */
14503 if (!mData->mCurrentSnapshot.isNull())
14504 {
14505 mData->mCurrentStateModified = TRUE;
14506 stsFlags |= SaveSTS_CurStateModified;
14507 }
14508 }
14509
14510 if (deleteSavedState)
14511 {
14512 if (mRemoveSavedState)
14513 {
14514 Assert(!mSSData->strStateFilePath.isEmpty());
14515
14516 // it is safe to delete the saved state file if ...
14517 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14518 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14519 // ... none of the snapshots share the saved state file
14520 )
14521 RTFileDelete(mSSData->strStateFilePath.c_str());
14522 }
14523
14524 mSSData->strStateFilePath.setNull();
14525 stsFlags |= SaveSTS_StateFilePath;
14526 }
14527
14528 /* redirect to the underlying peer machine */
14529 mPeer->setMachineState(aMachineState);
14530
14531 if ( aMachineState == MachineState_PoweredOff
14532 || aMachineState == MachineState_Teleported
14533 || aMachineState == MachineState_Aborted
14534 || aMachineState == MachineState_Saved)
14535 {
14536 /* the machine has stopped execution
14537 * (or the saved state file was adopted) */
14538 stsFlags |= SaveSTS_StateTimeStamp;
14539 }
14540
14541 if ( ( oldMachineState == MachineState_PoweredOff
14542 || oldMachineState == MachineState_Aborted
14543 || oldMachineState == MachineState_Teleported
14544 )
14545 && aMachineState == MachineState_Saved)
14546 {
14547 /* the saved state file was adopted */
14548 Assert(!mSSData->strStateFilePath.isEmpty());
14549 stsFlags |= SaveSTS_StateFilePath;
14550 }
14551
14552#ifdef VBOX_WITH_GUEST_PROPS
14553 if ( aMachineState == MachineState_PoweredOff
14554 || aMachineState == MachineState_Aborted
14555 || aMachineState == MachineState_Teleported)
14556 {
14557 /* Make sure any transient guest properties get removed from the
14558 * property store on shutdown. */
14559
14560 HWData::GuestPropertyMap::const_iterator it;
14561 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14562 if (!fNeedsSaving)
14563 for (it = mHWData->mGuestProperties.begin();
14564 it != mHWData->mGuestProperties.end(); ++it)
14565 if ( (it->second.mFlags & guestProp::TRANSIENT)
14566 || (it->second.mFlags & guestProp::TRANSRESET))
14567 {
14568 fNeedsSaving = true;
14569 break;
14570 }
14571 if (fNeedsSaving)
14572 {
14573 mData->mCurrentStateModified = TRUE;
14574 stsFlags |= SaveSTS_CurStateModified;
14575 }
14576 }
14577#endif
14578
14579 rc = saveStateSettings(stsFlags);
14580
14581 if ( ( oldMachineState != MachineState_PoweredOff
14582 && oldMachineState != MachineState_Aborted
14583 && oldMachineState != MachineState_Teleported
14584 )
14585 && ( aMachineState == MachineState_PoweredOff
14586 || aMachineState == MachineState_Aborted
14587 || aMachineState == MachineState_Teleported
14588 )
14589 )
14590 {
14591 /* we've been shut down for any reason */
14592 /* no special action so far */
14593 }
14594
14595 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14596 LogFlowThisFuncLeave();
14597 return rc;
14598}
14599
14600/**
14601 * Sends the current machine state value to the VM process.
14602 *
14603 * @note Locks this object for reading, then calls a client process.
14604 */
14605HRESULT SessionMachine::updateMachineStateOnClient()
14606{
14607 AutoCaller autoCaller(this);
14608 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14609
14610 ComPtr<IInternalSessionControl> directControl;
14611 {
14612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14613 AssertReturn(!!mData, E_FAIL);
14614 directControl = mData->mSession.mDirectControl;
14615
14616 /* directControl may be already set to NULL here in #OnSessionEnd()
14617 * called too early by the direct session process while there is still
14618 * some operation (like deleting the snapshot) in progress. The client
14619 * process in this case is waiting inside Session::close() for the
14620 * "end session" process object to complete, while #uninit() called by
14621 * #checkForDeath() on the Watcher thread is waiting for the pending
14622 * operation to complete. For now, we accept this inconsistent behavior
14623 * and simply do nothing here. */
14624
14625 if (mData->mSession.mState == SessionState_Unlocking)
14626 return S_OK;
14627
14628 AssertReturn(!directControl.isNull(), E_FAIL);
14629 }
14630
14631 return directControl->UpdateMachineState(mData->mMachineState);
14632}
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