VirtualBox

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

Last change on this file since 58420 was 58420, checked in by vboxsync, 10 years ago

Main/MachineImpl: be on the safe side and clear the list of attached PCI devices before filling

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 513.3 KB
Line 
1/* $Id: MachineImpl.cpp 58420 2015-10-26 14:53:32Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
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 HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->i_applyDefaults(aOsType);
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Apply parallel port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
358 mParallelPorts[slot]->i_applyDefaults();
359
360 /* Let the OS type select 64-bit ness. */
361 mHWData->mLongMode = aOsType->i_is64Bit()
362 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 }
364
365 /* At this point the changing of the current state modification
366 * flag is allowed. */
367 i_allowStateModification();
368
369 /* commit all changes made during the initialization */
370 i_commit();
371 }
372
373 /* Confirm a successful initialization when it's the case */
374 if (SUCCEEDED(rc))
375 {
376 if (mData->mAccessible)
377 autoInitSpan.setSucceeded();
378 else
379 autoInitSpan.setLimited();
380 }
381
382 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
383 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
384 mData->mRegistered,
385 mData->mAccessible,
386 rc));
387
388 LogFlowThisFuncLeave();
389
390 return rc;
391}
392
393/**
394 * Initializes a new instance with data from machine XML (formerly Init_Registered).
395 * Gets called in two modes:
396 *
397 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
398 * UUID is specified and we mark the machine as "registered";
399 *
400 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
401 * and the machine remains unregistered until RegisterMachine() is called.
402 *
403 * @param aParent Associated parent object
404 * @param aConfigFile Local file system path to the VM settings file (can
405 * be relative to the VirtualBox config directory).
406 * @param aId UUID of the machine or NULL (see above).
407 *
408 * @return Success indicator. if not S_OK, the machine object is invalid
409 */
410HRESULT Machine::initFromSettings(VirtualBox *aParent,
411 const Utf8Str &strConfigFile,
412 const Guid *aId)
413{
414 LogFlowThisFuncEnter();
415 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
416
417 /* Enclose the state transition NotReady->InInit->Ready */
418 AutoInitSpan autoInitSpan(this);
419 AssertReturn(autoInitSpan.isOk(), E_FAIL);
420
421 HRESULT rc = initImpl(aParent, strConfigFile);
422 if (FAILED(rc)) return rc;
423
424 if (aId)
425 {
426 // loading a registered VM:
427 unconst(mData->mUuid) = *aId;
428 mData->mRegistered = TRUE;
429 // now load the settings from XML:
430 rc = i_registeredInit();
431 // this calls initDataAndChildObjects() and loadSettings()
432 }
433 else
434 {
435 // opening an unregistered VM (VirtualBox::OpenMachine()):
436 rc = initDataAndChildObjects();
437
438 if (SUCCEEDED(rc))
439 {
440 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
441 mData->mAccessible = TRUE;
442
443 try
444 {
445 // load and parse machine XML; this will throw on XML or logic errors
446 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
447
448 // reject VM UUID duplicates, they can happen if someone
449 // tries to register an already known VM config again
450 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
451 true /* fPermitInaccessible */,
452 false /* aDoSetError */,
453 NULL) != VBOX_E_OBJECT_NOT_FOUND)
454 {
455 throw setError(E_FAIL,
456 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
457 mData->m_strConfigFile.c_str());
458 }
459
460 // use UUID from machine config
461 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
462
463 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
464 NULL /* puuidRegistry */);
465 if (FAILED(rc)) throw rc;
466
467 /* At this point the changing of the current state modification
468 * flag is allowed. */
469 i_allowStateModification();
470
471 i_commit();
472 }
473 catch (HRESULT err)
474 {
475 /* we assume that error info is set by the thrower */
476 rc = err;
477 }
478 catch (...)
479 {
480 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
481 }
482 }
483 }
484
485 /* Confirm a successful initialization when it's the case */
486 if (SUCCEEDED(rc))
487 {
488 if (mData->mAccessible)
489 autoInitSpan.setSucceeded();
490 else
491 {
492 autoInitSpan.setLimited();
493
494 // uninit media from this machine's media registry, or else
495 // reloading the settings will fail
496 mParent->i_unregisterMachineMedia(i_getId());
497 }
498 }
499
500 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
501 "rc=%08X\n",
502 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
503 mData->mRegistered, mData->mAccessible, rc));
504
505 LogFlowThisFuncLeave();
506
507 return rc;
508}
509
510/**
511 * Initializes a new instance from a machine config that is already in memory
512 * (import OVF case). Since we are importing, the UUID in the machine
513 * config is ignored and we always generate a fresh one.
514 *
515 * @param strName Name for the new machine; this overrides what is specified in config and is used
516 * for the settings file as well.
517 * @param config Machine configuration loaded and parsed from XML.
518 *
519 * @return Success indicator. if not S_OK, the machine object is invalid
520 */
521HRESULT Machine::init(VirtualBox *aParent,
522 const Utf8Str &strName,
523 const settings::MachineConfigFile &config)
524{
525 LogFlowThisFuncEnter();
526
527 /* Enclose the state transition NotReady->InInit->Ready */
528 AutoInitSpan autoInitSpan(this);
529 AssertReturn(autoInitSpan.isOk(), E_FAIL);
530
531 Utf8Str strConfigFile;
532 aParent->i_getDefaultMachineFolder(strConfigFile);
533 strConfigFile.append(RTPATH_DELIMITER);
534 strConfigFile.append(strName);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(".vbox");
538
539 HRESULT rc = initImpl(aParent, strConfigFile);
540 if (FAILED(rc)) return rc;
541
542 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
543 if (FAILED(rc)) return rc;
544
545 rc = initDataAndChildObjects();
546
547 if (SUCCEEDED(rc))
548 {
549 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
550 mData->mAccessible = TRUE;
551
552 // create empty machine config for instance data
553 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
554
555 // generate fresh UUID, ignore machine config
556 unconst(mData->mUuid).create();
557
558 rc = i_loadMachineDataFromSettings(config,
559 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
560
561 // override VM name as well, it may be different
562 mUserData->s.strName = strName;
563
564 if (SUCCEEDED(rc))
565 {
566 /* At this point the changing of the current state modification
567 * flag is allowed. */
568 i_allowStateModification();
569
570 /* commit all changes made during the initialization */
571 i_commit();
572 }
573 }
574
575 /* Confirm a successful initialization when it's the case */
576 if (SUCCEEDED(rc))
577 {
578 if (mData->mAccessible)
579 autoInitSpan.setSucceeded();
580 else
581 {
582 /* Ignore all errors from unregistering, they would destroy
583- * the more interesting error information we already have,
584- * pinpointing the issue with the VM config. */
585 ErrorInfoKeeper eik;
586
587 autoInitSpan.setLimited();
588
589 // uninit media from this machine's media registry, or else
590 // reloading the settings will fail
591 mParent->i_unregisterMachineMedia(i_getId());
592 }
593 }
594
595 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
596 "rc=%08X\n",
597 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
598 mData->mRegistered, mData->mAccessible, rc));
599
600 LogFlowThisFuncLeave();
601
602 return rc;
603}
604
605/**
606 * Shared code between the various init() implementations.
607 * @param aParent
608 * @return
609 */
610HRESULT Machine::initImpl(VirtualBox *aParent,
611 const Utf8Str &strConfigFile)
612{
613 LogFlowThisFuncEnter();
614
615 AssertReturn(aParent, E_INVALIDARG);
616 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
617
618 HRESULT rc = S_OK;
619
620 /* share the parent weakly */
621 unconst(mParent) = aParent;
622
623 /* allocate the essential machine data structure (the rest will be
624 * allocated later by initDataAndChildObjects() */
625 mData.allocate();
626
627 /* memorize the config file name (as provided) */
628 mData->m_strConfigFile = strConfigFile;
629
630 /* get the full file name */
631 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
632 if (RT_FAILURE(vrc1))
633 return setError(VBOX_E_FILE_ERROR,
634 tr("Invalid machine settings file name '%s' (%Rrc)"),
635 strConfigFile.c_str(),
636 vrc1);
637
638 LogFlowThisFuncLeave();
639
640 return rc;
641}
642
643/**
644 * Tries to create a machine settings file in the path stored in the machine
645 * instance data. Used when a new machine is created to fail gracefully if
646 * the settings file could not be written (e.g. because machine dir is read-only).
647 * @return
648 */
649HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
650{
651 HRESULT rc = S_OK;
652
653 // when we create a new machine, we must be able to create the settings file
654 RTFILE f = NIL_RTFILE;
655 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
656 if ( RT_SUCCESS(vrc)
657 || vrc == VERR_SHARING_VIOLATION
658 )
659 {
660 if (RT_SUCCESS(vrc))
661 RTFileClose(f);
662 if (!fForceOverwrite)
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Machine settings file '%s' already exists"),
665 mData->m_strConfigFileFull.c_str());
666 else
667 {
668 /* try to delete the config file, as otherwise the creation
669 * of a new settings file will fail. */
670 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
671 if (RT_FAILURE(vrc2))
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Could not delete the existing settings file '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(), vrc2);
675 }
676 }
677 else if ( vrc != VERR_FILE_NOT_FOUND
678 && vrc != VERR_PATH_NOT_FOUND
679 )
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Invalid machine settings file name '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(),
683 vrc);
684 return rc;
685}
686
687/**
688 * Initializes the registered machine by loading the settings file.
689 * This method is separated from #init() in order to make it possible to
690 * retry the operation after VirtualBox startup instead of refusing to
691 * startup the whole VirtualBox server in case if the settings file of some
692 * registered VM is invalid or inaccessible.
693 *
694 * @note Must be always called from this object's write lock
695 * (unless called from #init() that doesn't need any locking).
696 * @note Locks the mUSBController method for writing.
697 * @note Subclasses must not call this method.
698 */
699HRESULT Machine::i_registeredInit()
700{
701 AssertReturn(!i_isSessionMachine(), E_FAIL);
702 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
703 AssertReturn(mData->mUuid.isValid(), E_FAIL);
704 AssertReturn(!mData->mAccessible, E_FAIL);
705
706 HRESULT rc = initDataAndChildObjects();
707
708 if (SUCCEEDED(rc))
709 {
710 /* Temporarily reset the registered flag in order to let setters
711 * potentially called from loadSettings() succeed (isMutable() used in
712 * all setters will return FALSE for a Machine instance if mRegistered
713 * is TRUE). */
714 mData->mRegistered = FALSE;
715
716 try
717 {
718 // load and parse machine XML; this will throw on XML or logic errors
719 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
720
721 if (mData->mUuid != mData->pMachineConfigFile->uuid)
722 throw setError(E_FAIL,
723 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
724 mData->pMachineConfigFile->uuid.raw(),
725 mData->m_strConfigFileFull.c_str(),
726 mData->mUuid.toString().c_str(),
727 mParent->i_settingsFilePath().c_str());
728
729 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
730 NULL /* const Guid *puuidRegistry */);
731 if (FAILED(rc)) throw rc;
732 }
733 catch (HRESULT err)
734 {
735 /* we assume that error info is set by the thrower */
736 rc = err;
737 }
738 catch (...)
739 {
740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
741 }
742
743 /* Restore the registered flag (even on failure) */
744 mData->mRegistered = TRUE;
745 }
746
747 if (SUCCEEDED(rc))
748 {
749 /* Set mAccessible to TRUE only if we successfully locked and loaded
750 * the settings file */
751 mData->mAccessible = TRUE;
752
753 /* commit all changes made during loading the settings file */
754 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
755 /// @todo r=klaus for some reason the settings loading logic backs up
756 // the settings, and therefore a commit is needed. Should probably be changed.
757 }
758 else
759 {
760 /* If the machine is registered, then, instead of returning a
761 * failure, we mark it as inaccessible and set the result to
762 * success to give it a try later */
763
764 /* fetch the current error info */
765 mData->mAccessError = com::ErrorInfo();
766 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 i_rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->i_unregisterMachineMedia(i_getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 i_saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!i_isSnapshotMachine());
819 Assert(!i_isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it does
834 * VirtualBox::addCaller() for the duration of the
835 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 Log1WarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(i_getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->i_unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
878 i_rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// Wrapped IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
893{
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 aParent = pVirtualBox;
897
898 return S_OK;
899}
900
901
902HRESULT Machine::getAccessible(BOOL *aAccessible)
903{
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->i_dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = i_registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->i_onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
954{
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 aAccessError = errorInfo;
975 }
976
977 return rc;
978}
979
980HRESULT Machine::getName(com::Utf8Str &aName)
981{
982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
983
984 aName = mUserData->s.strName;
985
986 return S_OK;
987}
988
989HRESULT Machine::setName(const com::Utf8Str &aName)
990{
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994
995 if (test.isValid())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = i_checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 i_setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1011{
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 aDescription = mUserData->s.strDescription;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1020{
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 // this can be done in principle in any state as it doesn't affect the VM
1024 // significantly, but play safe by not messing around while complex
1025 // activities are going on
1026 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 i_setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::getId(com::Guid &aId)
1037{
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 aId = mData->mUuid;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048 aGroups.resize(mUserData->s.llGroups.size());
1049 size_t i = 0;
1050 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1051 it != mUserData->s.llGroups.end(); ++it, ++i)
1052 aGroups[i] = (*it);
1053
1054 return S_OK;
1055}
1056
1057HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1058{
1059 StringsList llGroups;
1060 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1061 if (FAILED(rc))
1062 return rc;
1063
1064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1065
1066 rc = i_checkStateDependency(MutableOrSavedStateDep);
1067 if (FAILED(rc)) return rc;
1068
1069 i_setModified(IsModified_MachineData);
1070 mUserData.backup();
1071 mUserData->s.llGroups = llGroups;
1072
1073 return S_OK;
1074}
1075
1076HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1077{
1078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1079
1080 aOSTypeId = mUserData->s.strOsType;
1081
1082 return S_OK;
1083}
1084
1085HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1086{
1087 /* look up the object by Id to check it is valid */
1088 ComPtr<IGuestOSType> guestOSType;
1089 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1090 if (FAILED(rc)) return rc;
1091
1092 /* when setting, always use the "etalon" value for consistency -- lookup
1093 * by ID is case-insensitive and the input value may have different case */
1094 Bstr osTypeId;
1095 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1096 if (FAILED(rc)) return rc;
1097
1098 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1099
1100 rc = i_checkStateDependency(MutableStateDep);
1101 if (FAILED(rc)) return rc;
1102
1103 i_setModified(IsModified_MachineData);
1104 mUserData.backup();
1105 mUserData->s.strOsType = osTypeId;
1106
1107 return S_OK;
1108}
1109
1110HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1111{
1112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1113
1114 *aFirmwareType = mHWData->mFirmwareType;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1120{
1121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 HRESULT rc = i_checkStateDependency(MutableStateDep);
1124 if (FAILED(rc)) return rc;
1125
1126 i_setModified(IsModified_MachineData);
1127 mHWData.backup();
1128 mHWData->mFirmwareType = aFirmwareType;
1129
1130 return S_OK;
1131}
1132
1133HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1134{
1135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1136
1137 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1143{
1144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 HRESULT rc = i_checkStateDependency(MutableStateDep);
1147 if (FAILED(rc)) return rc;
1148
1149 i_setModified(IsModified_MachineData);
1150 mHWData.backup();
1151 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1152
1153 return S_OK;
1154}
1155
1156HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1157{
1158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1159
1160 *aPointingHIDType = mHWData->mPointingHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1166{
1167 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 HRESULT rc = i_checkStateDependency(MutableStateDep);
1170 if (FAILED(rc)) return rc;
1171
1172 i_setModified(IsModified_MachineData);
1173 mHWData.backup();
1174 mHWData->mPointingHIDType = aPointingHIDType;
1175
1176 return S_OK;
1177}
1178
1179HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1180{
1181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1182
1183 *aChipsetType = mHWData->mChipsetType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1189{
1190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 HRESULT rc = i_checkStateDependency(MutableStateDep);
1193 if (FAILED(rc)) return rc;
1194
1195 if (aChipsetType != mHWData->mChipsetType)
1196 {
1197 i_setModified(IsModified_MachineData);
1198 mHWData.backup();
1199 mHWData->mChipsetType = aChipsetType;
1200
1201 // Resize network adapter array, to be finalized on commit/rollback.
1202 // We must not throw away entries yet, otherwise settings are lost
1203 // without a way to roll back.
1204 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1205 size_t oldCount = mNetworkAdapters.size();
1206 if (newCount > oldCount)
1207 {
1208 mNetworkAdapters.resize(newCount);
1209 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1210 {
1211 unconst(mNetworkAdapters[slot]).createObject();
1212 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1213 }
1214 }
1215 }
1216
1217 return S_OK;
1218}
1219
1220HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1221{
1222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 *aParavirtProvider = mHWData->mParavirtProvider;
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1230{
1231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 HRESULT rc = i_checkStateDependency(MutableStateDep);
1234 if (FAILED(rc)) return rc;
1235
1236 if (aParavirtProvider != mHWData->mParavirtProvider)
1237 {
1238 i_setModified(IsModified_MachineData);
1239 mHWData.backup();
1240 mHWData->mParavirtProvider = aParavirtProvider;
1241 }
1242
1243 return S_OK;
1244}
1245
1246HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1247{
1248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1249
1250 *aParavirtProvider = mHWData->mParavirtProvider;
1251 switch (mHWData->mParavirtProvider)
1252 {
1253 case ParavirtProvider_None:
1254 case ParavirtProvider_HyperV:
1255 case ParavirtProvider_KVM:
1256 case ParavirtProvider_Minimal:
1257 break;
1258
1259 /* Resolve dynamic provider types to the effective types. */
1260 default:
1261 {
1262 ComPtr<IGuestOSType> ptrGuestOSType;
1263 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1264 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1265
1266 Bstr guestTypeFamilyId;
1267 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1268 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1269 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1270
1271 switch (mHWData->mParavirtProvider)
1272 {
1273 case ParavirtProvider_Legacy:
1274 {
1275 if (fOsXGuest)
1276 *aParavirtProvider = ParavirtProvider_Minimal;
1277 else
1278 *aParavirtProvider = ParavirtProvider_None;
1279 break;
1280 }
1281
1282 case ParavirtProvider_Default:
1283 {
1284 if (fOsXGuest)
1285 *aParavirtProvider = ParavirtProvider_Minimal;
1286 else if ( mUserData->s.strOsType == "Windows10"
1287 || mUserData->s.strOsType == "Windows10_64"
1288 || mUserData->s.strOsType == "Windows81"
1289 || mUserData->s.strOsType == "Windows81_64"
1290 || mUserData->s.strOsType == "Windows8"
1291 || mUserData->s.strOsType == "Windows8_64"
1292 || mUserData->s.strOsType == "Windows7"
1293 || mUserData->s.strOsType == "Windows7_64"
1294 || mUserData->s.strOsType == "WindowsVista"
1295 || mUserData->s.strOsType == "WindowsVista_64"
1296 || mUserData->s.strOsType == "Windows2012"
1297 || mUserData->s.strOsType == "Windows2012_64"
1298 || mUserData->s.strOsType == "Windows2008"
1299 || mUserData->s.strOsType == "Windows2008_64")
1300 {
1301 *aParavirtProvider = ParavirtProvider_HyperV;
1302 }
1303 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1304 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1305 || mUserData->s.strOsType == "Linux"
1306 || mUserData->s.strOsType == "Linux_64"
1307 || mUserData->s.strOsType == "ArchLinux"
1308 || mUserData->s.strOsType == "ArchLinux_64"
1309 || mUserData->s.strOsType == "Debian"
1310 || mUserData->s.strOsType == "Debian_64"
1311 || mUserData->s.strOsType == "Fedora"
1312 || mUserData->s.strOsType == "Fedora_64"
1313 || mUserData->s.strOsType == "Gentoo"
1314 || mUserData->s.strOsType == "Gentoo_64"
1315 || mUserData->s.strOsType == "Mandriva"
1316 || mUserData->s.strOsType == "Mandriva_64"
1317 || mUserData->s.strOsType == "OpenSUSE"
1318 || mUserData->s.strOsType == "OpenSUSE_64"
1319 || mUserData->s.strOsType == "Oracle"
1320 || mUserData->s.strOsType == "Oracle_64"
1321 || mUserData->s.strOsType == "RedHat"
1322 || mUserData->s.strOsType == "RedHat_64"
1323 || mUserData->s.strOsType == "Turbolinux"
1324 || mUserData->s.strOsType == "Turbolinux_64"
1325 || mUserData->s.strOsType == "Ubuntu"
1326 || mUserData->s.strOsType == "Ubuntu_64"
1327 || mUserData->s.strOsType == "Xandros"
1328 || mUserData->s.strOsType == "Xandros_64")
1329 {
1330 *aParavirtProvider = ParavirtProvider_KVM;
1331 }
1332 else
1333 *aParavirtProvider = ParavirtProvider_None;
1334 break;
1335 }
1336 }
1337 break;
1338 }
1339 }
1340
1341 Assert( *aParavirtProvider == ParavirtProvider_None
1342 || *aParavirtProvider == ParavirtProvider_Minimal
1343 || *aParavirtProvider == ParavirtProvider_HyperV
1344 || *aParavirtProvider == ParavirtProvider_KVM);
1345 return S_OK;
1346}
1347
1348HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1349{
1350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1351
1352 aHardwareVersion = mHWData->mHWVersion;
1353
1354 return S_OK;
1355}
1356
1357HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1358{
1359 /* check known version */
1360 Utf8Str hwVersion = aHardwareVersion;
1361 if ( hwVersion.compare("1") != 0
1362 && hwVersion.compare("2") != 0)
1363 return setError(E_INVALIDARG,
1364 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = i_checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 i_setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 mHWData->mHWVersion = aHardwareVersion;
1374
1375 return S_OK;
1376}
1377
1378HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1379{
1380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1381
1382 if (!mHWData->mHardwareUUID.isZero())
1383 aHardwareUUID = mHWData->mHardwareUUID;
1384 else
1385 aHardwareUUID = mData->mUuid;
1386
1387 return S_OK;
1388}
1389
1390HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1391{
1392 if (!aHardwareUUID.isValid())
1393 return E_INVALIDARG;
1394
1395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 HRESULT rc = i_checkStateDependency(MutableStateDep);
1398 if (FAILED(rc)) return rc;
1399
1400 i_setModified(IsModified_MachineData);
1401 mHWData.backup();
1402 if (aHardwareUUID == mData->mUuid)
1403 mHWData->mHardwareUUID.clear();
1404 else
1405 mHWData->mHardwareUUID = aHardwareUUID;
1406
1407 return S_OK;
1408}
1409
1410HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1411{
1412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1413
1414 *aMemorySize = mHWData->mMemorySize;
1415
1416 return S_OK;
1417}
1418
1419HRESULT Machine::setMemorySize(ULONG aMemorySize)
1420{
1421 /* check RAM limits */
1422 if ( aMemorySize < MM_RAM_MIN_IN_MB
1423 || aMemorySize > MM_RAM_MAX_IN_MB
1424 )
1425 return setError(E_INVALIDARG,
1426 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1427 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1428
1429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1430
1431 HRESULT rc = i_checkStateDependency(MutableStateDep);
1432 if (FAILED(rc)) return rc;
1433
1434 i_setModified(IsModified_MachineData);
1435 mHWData.backup();
1436 mHWData->mMemorySize = aMemorySize;
1437
1438 return S_OK;
1439}
1440
1441HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1442{
1443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 *aCPUCount = mHWData->mCPUCount;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::setCPUCount(ULONG aCPUCount)
1451{
1452 /* check CPU limits */
1453 if ( aCPUCount < SchemaDefs::MinCPUCount
1454 || aCPUCount > SchemaDefs::MaxCPUCount
1455 )
1456 return setError(E_INVALIDARG,
1457 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1458 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1463 if (mHWData->mCPUHotPlugEnabled)
1464 {
1465 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1466 {
1467 if (mHWData->mCPUAttached[idx])
1468 return setError(E_INVALIDARG,
1469 tr("There is still a CPU attached to socket %lu."
1470 "Detach the CPU before removing the socket"),
1471 aCPUCount, idx+1);
1472 }
1473 }
1474
1475 HRESULT rc = i_checkStateDependency(MutableStateDep);
1476 if (FAILED(rc)) return rc;
1477
1478 i_setModified(IsModified_MachineData);
1479 mHWData.backup();
1480 mHWData->mCPUCount = aCPUCount;
1481
1482 return S_OK;
1483}
1484
1485HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1486{
1487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1488
1489 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1490
1491 return S_OK;
1492}
1493
1494HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1495{
1496 HRESULT rc = S_OK;
1497
1498 /* check throttle limits */
1499 if ( aCPUExecutionCap < 1
1500 || aCPUExecutionCap > 100
1501 )
1502 return setError(E_INVALIDARG,
1503 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1504 aCPUExecutionCap, 1, 100);
1505
1506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1507
1508 alock.release();
1509 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1510 alock.acquire();
1511 if (FAILED(rc)) return rc;
1512
1513 i_setModified(IsModified_MachineData);
1514 mHWData.backup();
1515 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1516
1517 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1518 if (Global::IsOnline(mData->mMachineState))
1519 i_saveSettings(NULL);
1520
1521 return S_OK;
1522}
1523
1524HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1525{
1526 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1527
1528 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1529
1530 return S_OK;
1531}
1532
1533HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1534{
1535 HRESULT rc = S_OK;
1536
1537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 rc = i_checkStateDependency(MutableStateDep);
1540 if (FAILED(rc)) return rc;
1541
1542 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1543 {
1544 if (aCPUHotPlugEnabled)
1545 {
1546 i_setModified(IsModified_MachineData);
1547 mHWData.backup();
1548
1549 /* Add the amount of CPUs currently attached */
1550 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1551 mHWData->mCPUAttached[i] = true;
1552 }
1553 else
1554 {
1555 /*
1556 * We can disable hotplug only if the amount of maximum CPUs is equal
1557 * to the amount of attached CPUs
1558 */
1559 unsigned cCpusAttached = 0;
1560 unsigned iHighestId = 0;
1561
1562 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1563 {
1564 if (mHWData->mCPUAttached[i])
1565 {
1566 cCpusAttached++;
1567 iHighestId = i;
1568 }
1569 }
1570
1571 if ( (cCpusAttached != mHWData->mCPUCount)
1572 || (iHighestId >= mHWData->mCPUCount))
1573 return setError(E_INVALIDARG,
1574 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1575
1576 i_setModified(IsModified_MachineData);
1577 mHWData.backup();
1578 }
1579 }
1580
1581 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1582
1583 return rc;
1584}
1585
1586HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1587{
1588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1589
1590 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1591
1592 return S_OK;
1593}
1594
1595HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1596{
1597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1598
1599 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1600 if (SUCCEEDED(hrc))
1601 {
1602 i_setModified(IsModified_MachineData);
1603 mHWData.backup();
1604 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1605 }
1606 return hrc;
1607}
1608
1609HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1610{
1611#ifdef VBOX_WITH_USB_CARDREADER
1612 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1613
1614 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1615
1616 return S_OK;
1617#else
1618 NOREF(aEmulatedUSBCardReaderEnabled);
1619 return E_NOTIMPL;
1620#endif
1621}
1622
1623HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1624{
1625#ifdef VBOX_WITH_USB_CARDREADER
1626 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1627
1628 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1629 if (FAILED(rc)) return rc;
1630
1631 i_setModified(IsModified_MachineData);
1632 mHWData.backup();
1633 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1634
1635 return S_OK;
1636#else
1637 NOREF(aEmulatedUSBCardReaderEnabled);
1638 return E_NOTIMPL;
1639#endif
1640}
1641
1642HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1643{
1644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1645
1646 *aHPETEnabled = mHWData->mHPETEnabled;
1647
1648 return S_OK;
1649}
1650
1651HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1652{
1653 HRESULT rc = S_OK;
1654
1655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1656
1657 rc = i_checkStateDependency(MutableStateDep);
1658 if (FAILED(rc)) return rc;
1659
1660 i_setModified(IsModified_MachineData);
1661 mHWData.backup();
1662
1663 mHWData->mHPETEnabled = aHPETEnabled;
1664
1665 return rc;
1666}
1667
1668HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1669{
1670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1671
1672 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1673 return S_OK;
1674}
1675
1676HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1677{
1678 HRESULT rc = S_OK;
1679
1680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1681
1682 i_setModified(IsModified_MachineData);
1683 mHWData.backup();
1684 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1685
1686 alock.release();
1687 rc = i_onVideoCaptureChange();
1688 alock.acquire();
1689 if (FAILED(rc))
1690 {
1691 /*
1692 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1693 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1694 * determine if it should start or stop capturing. Therefore we need to manually
1695 * undo change.
1696 */
1697 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1698 return rc;
1699 }
1700
1701 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1702 if (Global::IsOnline(mData->mMachineState))
1703 i_saveSettings(NULL);
1704
1705 return rc;
1706}
1707
1708HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1709{
1710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1711 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1712 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1713 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1714 return S_OK;
1715}
1716
1717HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1718{
1719 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1720 bool fChanged = false;
1721
1722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1725 {
1726 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1727 {
1728 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1729 fChanged = true;
1730 }
1731 }
1732 if (fChanged)
1733 {
1734 alock.release();
1735 HRESULT rc = i_onVideoCaptureChange();
1736 alock.acquire();
1737 if (FAILED(rc)) return rc;
1738 i_setModified(IsModified_MachineData);
1739
1740 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1741 if (Global::IsOnline(mData->mMachineState))
1742 i_saveSettings(NULL);
1743 }
1744
1745 return S_OK;
1746}
1747
1748HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1749{
1750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1751 if (mHWData->mVideoCaptureFile.isEmpty())
1752 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1753 else
1754 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1755 return S_OK;
1756}
1757
1758HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1759{
1760 Utf8Str strFile(aVideoCaptureFile);
1761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1762
1763 if ( Global::IsOnline(mData->mMachineState)
1764 && mHWData->mVideoCaptureEnabled)
1765 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1766
1767 if (!RTPathStartsWithRoot(strFile.c_str()))
1768 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1769
1770 if (!strFile.isEmpty())
1771 {
1772 Utf8Str defaultFile;
1773 i_getDefaultVideoCaptureFile(defaultFile);
1774 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1775 strFile.setNull();
1776 }
1777
1778 i_setModified(IsModified_MachineData);
1779 mHWData.backup();
1780 mHWData->mVideoCaptureFile = strFile;
1781
1782 return S_OK;
1783}
1784
1785HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1786{
1787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1788 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1789 return S_OK;
1790}
1791
1792HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1793{
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 if ( Global::IsOnline(mData->mMachineState)
1797 && mHWData->mVideoCaptureEnabled)
1798 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1799
1800 i_setModified(IsModified_MachineData);
1801 mHWData.backup();
1802 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1803
1804 return S_OK;
1805}
1806
1807HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1811 return S_OK;
1812}
1813
1814HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1815{
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 i_setModified(IsModified_MachineData);
1823 mHWData.backup();
1824 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1833 return S_OK;
1834}
1835
1836HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1837{
1838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 if ( Global::IsOnline(mData->mMachineState)
1841 && mHWData->mVideoCaptureEnabled)
1842 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1843
1844 i_setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1847
1848 return S_OK;
1849}
1850
1851HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1852{
1853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1854 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1855 return S_OK;
1856}
1857
1858HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1859{
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 if ( Global::IsOnline(mData->mMachineState)
1863 && mHWData->mVideoCaptureEnabled)
1864 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1865
1866 i_setModified(IsModified_MachineData);
1867 mHWData.backup();
1868 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1869
1870 return S_OK;
1871}
1872
1873HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1874{
1875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1876 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1877 return S_OK;
1878}
1879
1880HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1881{
1882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 if ( Global::IsOnline(mData->mMachineState)
1885 && mHWData->mVideoCaptureEnabled)
1886 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1887
1888 i_setModified(IsModified_MachineData);
1889 mHWData.backup();
1890 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1896{
1897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1898 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1899 return S_OK;
1900}
1901
1902HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1903{
1904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1905
1906 if ( Global::IsOnline(mData->mMachineState)
1907 && mHWData->mVideoCaptureEnabled)
1908 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1909
1910 i_setModified(IsModified_MachineData);
1911 mHWData.backup();
1912 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1913
1914 return S_OK;
1915}
1916
1917HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1918{
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1922 return S_OK;
1923}
1924
1925HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1926{
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 if ( Global::IsOnline(mData->mMachineState)
1930 && mHWData->mVideoCaptureEnabled)
1931 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1932
1933 i_setModified(IsModified_MachineData);
1934 mHWData.backup();
1935 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1936
1937 return S_OK;
1938}
1939
1940HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1941{
1942 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1943
1944 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1945
1946 return S_OK;
1947}
1948
1949HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1950{
1951 switch (aGraphicsControllerType)
1952 {
1953 case GraphicsControllerType_Null:
1954 case GraphicsControllerType_VBoxVGA:
1955#ifdef VBOX_WITH_VMSVGA
1956 case GraphicsControllerType_VMSVGA:
1957#endif
1958 break;
1959 default:
1960 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1961 }
1962
1963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 HRESULT rc = i_checkStateDependency(MutableStateDep);
1966 if (FAILED(rc)) return rc;
1967
1968 i_setModified(IsModified_MachineData);
1969 mHWData.backup();
1970 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1971
1972 return S_OK;
1973}
1974
1975HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1976{
1977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1978
1979 *aVRAMSize = mHWData->mVRAMSize;
1980
1981 return S_OK;
1982}
1983
1984HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1985{
1986 /* check VRAM limits */
1987 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1988 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1989 return setError(E_INVALIDARG,
1990 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1991 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1992
1993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1994
1995 HRESULT rc = i_checkStateDependency(MutableStateDep);
1996 if (FAILED(rc)) return rc;
1997
1998 i_setModified(IsModified_MachineData);
1999 mHWData.backup();
2000 mHWData->mVRAMSize = aVRAMSize;
2001
2002 return S_OK;
2003}
2004
2005/** @todo this method should not be public */
2006HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2007{
2008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2009
2010 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2011
2012 return S_OK;
2013}
2014
2015/**
2016 * Set the memory balloon size.
2017 *
2018 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2019 * we have to make sure that we never call IGuest from here.
2020 */
2021HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2022{
2023 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2024#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2025 /* check limits */
2026 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2027 return setError(E_INVALIDARG,
2028 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2029 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2030
2031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2032
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2036
2037 return S_OK;
2038#else
2039 NOREF(aMemoryBalloonSize);
2040 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2041#endif
2042}
2043
2044HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2045{
2046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2047
2048 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2049 return S_OK;
2050}
2051
2052HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2053{
2054#ifdef VBOX_WITH_PAGE_SHARING
2055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2056
2057 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2058 i_setModified(IsModified_MachineData);
2059 mHWData.backup();
2060 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2061 return S_OK;
2062#else
2063 NOREF(aPageFusionEnabled);
2064 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2065#endif
2066}
2067
2068HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2069{
2070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2071
2072 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2073
2074 return S_OK;
2075}
2076
2077HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2078{
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 HRESULT rc = i_checkStateDependency(MutableStateDep);
2082 if (FAILED(rc)) return rc;
2083
2084 /** @todo check validity! */
2085
2086 i_setModified(IsModified_MachineData);
2087 mHWData.backup();
2088 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2089
2090 return S_OK;
2091}
2092
2093
2094HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2095{
2096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2097
2098 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2099
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2104{
2105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2106
2107 HRESULT rc = i_checkStateDependency(MutableStateDep);
2108 if (FAILED(rc)) return rc;
2109
2110 /** @todo check validity! */
2111 i_setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2114
2115 return S_OK;
2116}
2117
2118HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2119{
2120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2121
2122 *aMonitorCount = mHWData->mMonitorCount;
2123
2124 return S_OK;
2125}
2126
2127HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2128{
2129 /* make sure monitor count is a sensible number */
2130 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2131 return setError(E_INVALIDARG,
2132 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2133 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2134
2135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2136
2137 HRESULT rc = i_checkStateDependency(MutableStateDep);
2138 if (FAILED(rc)) return rc;
2139
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mMonitorCount = aMonitorCount;
2143
2144 return S_OK;
2145}
2146
2147HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2148{
2149 /* mBIOSSettings is constant during life time, no need to lock */
2150 aBIOSSettings = mBIOSSettings;
2151
2152 return S_OK;
2153}
2154
2155HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2156{
2157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2158
2159 switch (aProperty)
2160 {
2161 case CPUPropertyType_PAE:
2162 *aValue = mHWData->mPAEEnabled;
2163 break;
2164
2165 case CPUPropertyType_LongMode:
2166 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2167 *aValue = TRUE;
2168 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2169 *aValue = FALSE;
2170#if HC_ARCH_BITS == 64
2171 else
2172 *aValue = TRUE;
2173#else
2174 else
2175 {
2176 *aValue = FALSE;
2177
2178 ComPtr<IGuestOSType> ptrGuestOSType;
2179 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2180 if (SUCCEEDED(hrc2))
2181 {
2182 BOOL fIs64Bit = FALSE;
2183 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2184 if (SUCCEEDED(hrc2) && fIs64Bit)
2185 {
2186 ComObjPtr<Host> ptrHost = mParent->i_host();
2187 alock.release();
2188
2189 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2190 if (FAILED(hrc2))
2191 *aValue = FALSE;
2192 }
2193 }
2194 }
2195#endif
2196 break;
2197
2198 case CPUPropertyType_TripleFaultReset:
2199 *aValue = mHWData->mTripleFaultReset;
2200 break;
2201
2202 default:
2203 return E_INVALIDARG;
2204 }
2205 return S_OK;
2206}
2207
2208HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2209{
2210 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2211
2212 HRESULT rc = i_checkStateDependency(MutableStateDep);
2213 if (FAILED(rc)) return rc;
2214
2215 switch (aProperty)
2216 {
2217 case CPUPropertyType_PAE:
2218 i_setModified(IsModified_MachineData);
2219 mHWData.backup();
2220 mHWData->mPAEEnabled = !!aValue;
2221 break;
2222
2223 case CPUPropertyType_LongMode:
2224 i_setModified(IsModified_MachineData);
2225 mHWData.backup();
2226 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2227 break;
2228
2229 case CPUPropertyType_TripleFaultReset:
2230 i_setModified(IsModified_MachineData);
2231 mHWData.backup();
2232 mHWData->mTripleFaultReset = !!aValue;
2233 break;
2234
2235 default:
2236 return E_INVALIDARG;
2237 }
2238 return S_OK;
2239}
2240
2241HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2242{
2243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2244
2245 switch(aId)
2246 {
2247 case 0x0:
2248 case 0x1:
2249 case 0x2:
2250 case 0x3:
2251 case 0x4:
2252 case 0x5:
2253 case 0x6:
2254 case 0x7:
2255 case 0x8:
2256 case 0x9:
2257 case 0xA:
2258 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2259 return E_INVALIDARG;
2260
2261 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2262 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2263 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2264 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2265 break;
2266
2267 case 0x80000000:
2268 case 0x80000001:
2269 case 0x80000002:
2270 case 0x80000003:
2271 case 0x80000004:
2272 case 0x80000005:
2273 case 0x80000006:
2274 case 0x80000007:
2275 case 0x80000008:
2276 case 0x80000009:
2277 case 0x8000000A:
2278 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2279 return E_INVALIDARG;
2280
2281 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2282 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2283 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2284 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2285 break;
2286
2287 default:
2288 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2289 }
2290 return S_OK;
2291}
2292
2293
2294HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2295{
2296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2297
2298 HRESULT rc = i_checkStateDependency(MutableStateDep);
2299 if (FAILED(rc)) return rc;
2300
2301 switch(aId)
2302 {
2303 case 0x0:
2304 case 0x1:
2305 case 0x2:
2306 case 0x3:
2307 case 0x4:
2308 case 0x5:
2309 case 0x6:
2310 case 0x7:
2311 case 0x8:
2312 case 0x9:
2313 case 0xA:
2314 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2315 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2316 i_setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2319 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2320 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2321 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2322 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2323 break;
2324
2325 case 0x80000000:
2326 case 0x80000001:
2327 case 0x80000002:
2328 case 0x80000003:
2329 case 0x80000004:
2330 case 0x80000005:
2331 case 0x80000006:
2332 case 0x80000007:
2333 case 0x80000008:
2334 case 0x80000009:
2335 case 0x8000000A:
2336 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2337 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2338 i_setModified(IsModified_MachineData);
2339 mHWData.backup();
2340 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2341 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2342 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2343 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2344 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2345 break;
2346
2347 default:
2348 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2349 }
2350 return S_OK;
2351}
2352
2353HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2354{
2355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2356
2357 HRESULT rc = i_checkStateDependency(MutableStateDep);
2358 if (FAILED(rc)) return rc;
2359
2360 switch(aId)
2361 {
2362 case 0x0:
2363 case 0x1:
2364 case 0x2:
2365 case 0x3:
2366 case 0x4:
2367 case 0x5:
2368 case 0x6:
2369 case 0x7:
2370 case 0x8:
2371 case 0x9:
2372 case 0xA:
2373 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2374 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2375 i_setModified(IsModified_MachineData);
2376 mHWData.backup();
2377 /* Invalidate leaf. */
2378 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2379 break;
2380
2381 case 0x80000000:
2382 case 0x80000001:
2383 case 0x80000002:
2384 case 0x80000003:
2385 case 0x80000004:
2386 case 0x80000005:
2387 case 0x80000006:
2388 case 0x80000007:
2389 case 0x80000008:
2390 case 0x80000009:
2391 case 0x8000000A:
2392 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2393 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2394 i_setModified(IsModified_MachineData);
2395 mHWData.backup();
2396 /* Invalidate leaf. */
2397 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2398 break;
2399
2400 default:
2401 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2402 }
2403 return S_OK;
2404}
2405
2406HRESULT Machine::removeAllCPUIDLeaves()
2407{
2408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2409
2410 HRESULT rc = i_checkStateDependency(MutableStateDep);
2411 if (FAILED(rc)) return rc;
2412
2413 i_setModified(IsModified_MachineData);
2414 mHWData.backup();
2415
2416 /* Invalidate all standard leafs. */
2417 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2418 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2419
2420 /* Invalidate all extended leafs. */
2421 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2422 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2423
2424 return S_OK;
2425}
2426HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2427{
2428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2429
2430 switch(aProperty)
2431 {
2432 case HWVirtExPropertyType_Enabled:
2433 *aValue = mHWData->mHWVirtExEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_VPID:
2437 *aValue = mHWData->mHWVirtExVPIDEnabled;
2438 break;
2439
2440 case HWVirtExPropertyType_NestedPaging:
2441 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2442 break;
2443
2444 case HWVirtExPropertyType_UnrestrictedExecution:
2445 *aValue = mHWData->mHWVirtExUXEnabled;
2446 break;
2447
2448 case HWVirtExPropertyType_LargePages:
2449 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2450#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2451 *aValue = FALSE;
2452#endif
2453 break;
2454
2455 case HWVirtExPropertyType_Force:
2456 *aValue = mHWData->mHWVirtExForceEnabled;
2457 break;
2458
2459 default:
2460 return E_INVALIDARG;
2461 }
2462 return S_OK;
2463}
2464
2465HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2466{
2467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 HRESULT rc = i_checkStateDependency(MutableStateDep);
2470 if (FAILED(rc)) return rc;
2471
2472 switch(aProperty)
2473 {
2474 case HWVirtExPropertyType_Enabled:
2475 i_setModified(IsModified_MachineData);
2476 mHWData.backup();
2477 mHWData->mHWVirtExEnabled = !!aValue;
2478 break;
2479
2480 case HWVirtExPropertyType_VPID:
2481 i_setModified(IsModified_MachineData);
2482 mHWData.backup();
2483 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2484 break;
2485
2486 case HWVirtExPropertyType_NestedPaging:
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2490 break;
2491
2492 case HWVirtExPropertyType_UnrestrictedExecution:
2493 i_setModified(IsModified_MachineData);
2494 mHWData.backup();
2495 mHWData->mHWVirtExUXEnabled = !!aValue;
2496 break;
2497
2498 case HWVirtExPropertyType_LargePages:
2499 i_setModified(IsModified_MachineData);
2500 mHWData.backup();
2501 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2502 break;
2503
2504 case HWVirtExPropertyType_Force:
2505 i_setModified(IsModified_MachineData);
2506 mHWData.backup();
2507 mHWData->mHWVirtExForceEnabled = !!aValue;
2508 break;
2509
2510 default:
2511 return E_INVALIDARG;
2512 }
2513
2514 return S_OK;
2515}
2516
2517HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2518{
2519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2520
2521 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2522
2523 return S_OK;
2524}
2525
2526HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2527{
2528 /* @todo (r=dmik):
2529 * 1. Allow to change the name of the snapshot folder containing snapshots
2530 * 2. Rename the folder on disk instead of just changing the property
2531 * value (to be smart and not to leave garbage). Note that it cannot be
2532 * done here because the change may be rolled back. Thus, the right
2533 * place is #saveSettings().
2534 */
2535
2536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2537
2538 HRESULT rc = i_checkStateDependency(MutableStateDep);
2539 if (FAILED(rc)) return rc;
2540
2541 if (!mData->mCurrentSnapshot.isNull())
2542 return setError(E_FAIL,
2543 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2544
2545 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2546
2547 if (strSnapshotFolder.isEmpty())
2548 strSnapshotFolder = "Snapshots";
2549 int vrc = i_calculateFullPath(strSnapshotFolder,
2550 strSnapshotFolder);
2551 if (RT_FAILURE(vrc))
2552 return setError(E_FAIL,
2553 tr("Invalid snapshot folder '%s' (%Rrc)"),
2554 strSnapshotFolder.c_str(), vrc);
2555
2556 i_setModified(IsModified_MachineData);
2557 mUserData.backup();
2558
2559 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2565{
2566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2567
2568 aMediumAttachments.resize(mMediaData->mAttachments.size());
2569 size_t i = 0;
2570 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2571 it != mMediaData->mAttachments.end(); ++it, ++i)
2572 aMediumAttachments[i] = *it;
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 Assert(!!mVRDEServer);
2582
2583 aVRDEServer = mVRDEServer;
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2589{
2590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2591
2592 aAudioAdapter = mAudioAdapter;
2593
2594 return S_OK;
2595}
2596
2597HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2598{
2599#ifdef VBOX_WITH_VUSB
2600 clearError();
2601 MultiResult rc(S_OK);
2602
2603# ifdef VBOX_WITH_USB
2604 rc = mParent->i_host()->i_checkUSBProxyService();
2605 if (FAILED(rc)) return rc;
2606# endif
2607
2608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2609
2610 USBControllerList data = *mUSBControllers.data();
2611 aUSBControllers.resize(data.size());
2612 size_t i = 0;
2613 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2614 aUSBControllers[i] = *it;
2615
2616 return S_OK;
2617#else
2618 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2619 * extended error info to indicate that USB is simply not available
2620 * (w/o treating it as a failure), for example, as in OSE */
2621 NOREF(aUSBControllers);
2622 ReturnComNotImplemented();
2623#endif /* VBOX_WITH_VUSB */
2624}
2625
2626HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2627{
2628#ifdef VBOX_WITH_VUSB
2629 clearError();
2630 MultiResult rc(S_OK);
2631
2632# ifdef VBOX_WITH_USB
2633 rc = mParent->i_host()->i_checkUSBProxyService();
2634 if (FAILED(rc)) return rc;
2635# endif
2636
2637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2638
2639 aUSBDeviceFilters = mUSBDeviceFilters;
2640 return rc;
2641#else
2642 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2643 * extended error info to indicate that USB is simply not available
2644 * (w/o treating it as a failure), for example, as in OSE */
2645 NOREF(aUSBDeviceFilters);
2646 ReturnComNotImplemented();
2647#endif /* VBOX_WITH_VUSB */
2648}
2649
2650HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2651{
2652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2653
2654 aSettingsFilePath = mData->m_strConfigFileFull;
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2664 if (FAILED(rc)) return rc;
2665
2666 if (!mData->pMachineConfigFile->fileExists())
2667 // this is a new machine, and no config file exists yet:
2668 *aSettingsModified = TRUE;
2669 else
2670 *aSettingsModified = (mData->flModifications != 0);
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 *aSessionState = mData->mSession.mState;
2680
2681 return S_OK;
2682}
2683
2684HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2685{
2686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2687
2688 aSessionName = mData->mSession.mName;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 *aSessionPID = mData->mSession.mPID;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getState(MachineState_T *aState)
2703{
2704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2705
2706 *aState = mData->mMachineState;
2707 Assert(mData->mMachineState != MachineState_Null);
2708
2709 return S_OK;
2710}
2711
2712HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2713{
2714 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2717
2718 return S_OK;
2719}
2720
2721HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2722{
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 aStateFilePath = mSSData->strStateFilePath;
2726
2727 return S_OK;
2728}
2729
2730HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2731{
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 i_getLogFolder(aLogFolder);
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aCurrentSnapshot = mData->mCurrentSnapshot;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2749{
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2753 ? 0
2754 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 /* Note: for machines with no snapshots, we always return FALSE
2764 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2765 * reasons :) */
2766
2767 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2768 ? FALSE
2769 : mData->mCurrentStateModified;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 aSharedFolders.resize(mHWData->mSharedFolders.size());
2779 size_t i = 0;
2780 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2781 it != mHWData->mSharedFolders.end(); ++i, ++it)
2782 aSharedFolders[i] = *it;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2788{
2789 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 *aClipboardMode = mHWData->mClipboardMode;
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2797{
2798 HRESULT rc = S_OK;
2799
2800 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 alock.release();
2803 rc = i_onClipboardModeChange(aClipboardMode);
2804 alock.acquire();
2805 if (FAILED(rc)) return rc;
2806
2807 i_setModified(IsModified_MachineData);
2808 mHWData.backup();
2809 mHWData->mClipboardMode = aClipboardMode;
2810
2811 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2812 if (Global::IsOnline(mData->mMachineState))
2813 i_saveSettings(NULL);
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aDnDMode = mHWData->mDnDMode;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2828{
2829 HRESULT rc = S_OK;
2830
2831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 alock.release();
2834 rc = i_onDnDModeChange(aDnDMode);
2835
2836 alock.acquire();
2837 if (FAILED(rc)) return rc;
2838
2839 i_setModified(IsModified_MachineData);
2840 mHWData.backup();
2841 mHWData->mDnDMode = aDnDMode;
2842
2843 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2844 if (Global::IsOnline(mData->mMachineState))
2845 i_saveSettings(NULL);
2846
2847 return S_OK;
2848}
2849
2850HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2851{
2852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2853 StorageControllerList data = *mStorageControllers.data();
2854 size_t i = 0;
2855 aStorageControllers.resize(data.size());
2856 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2857 aStorageControllers[i] = *it;
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864
2865 *aEnabled = mUserData->s.fTeleporterEnabled;
2866
2867 return S_OK;
2868}
2869
2870HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2871{
2872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2873
2874 /* Only allow it to be set to true when PoweredOff or Aborted.
2875 (Clearing it is always permitted.) */
2876 if ( aTeleporterEnabled
2877 && mData->mRegistered
2878 && ( !i_isSessionMachine()
2879 || ( mData->mMachineState != MachineState_PoweredOff
2880 && mData->mMachineState != MachineState_Teleported
2881 && mData->mMachineState != MachineState_Aborted
2882 )
2883 )
2884 )
2885 return setError(VBOX_E_INVALID_VM_STATE,
2886 tr("The machine is not powered off (state is %s)"),
2887 Global::stringifyMachineState(mData->mMachineState));
2888
2889 i_setModified(IsModified_MachineData);
2890 mUserData.backup();
2891 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2892
2893 return S_OK;
2894}
2895
2896HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2897{
2898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2901
2902 return S_OK;
2903}
2904
2905HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2906{
2907 if (aTeleporterPort >= _64K)
2908 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2909
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2913 if (FAILED(rc)) return rc;
2914
2915 i_setModified(IsModified_MachineData);
2916 mUserData.backup();
2917 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2923{
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2927
2928 return S_OK;
2929}
2930
2931HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2932{
2933 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2936 if (FAILED(rc)) return rc;
2937
2938 i_setModified(IsModified_MachineData);
2939 mUserData.backup();
2940 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2941
2942 return S_OK;
2943}
2944
2945HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2946{
2947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2948 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2949
2950 return S_OK;
2951}
2952
2953HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2954{
2955 /*
2956 * Hash the password first.
2957 */
2958 com::Utf8Str aT = aTeleporterPassword;
2959
2960 if (!aT.isEmpty())
2961 {
2962 if (VBoxIsPasswordHashed(&aT))
2963 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2964 VBoxHashPassword(&aT);
2965 }
2966
2967 /*
2968 * Do the update.
2969 */
2970 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2971 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2972 if (SUCCEEDED(hrc))
2973 {
2974 i_setModified(IsModified_MachineData);
2975 mUserData.backup();
2976 mUserData->s.strTeleporterPassword = aT;
2977 }
2978
2979 return hrc;
2980}
2981
2982HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2983{
2984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2987 return S_OK;
2988}
2989
2990HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2991{
2992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2993
2994 /* @todo deal with running state change. */
2995 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2996 if (FAILED(rc)) return rc;
2997
2998 i_setModified(IsModified_MachineData);
2999 mUserData.backup();
3000 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3001 return S_OK;
3002}
3003
3004HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3005{
3006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3007
3008 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3009 return S_OK;
3010}
3011
3012HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3013{
3014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 /* @todo deal with running state change. */
3017 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3018 if (FAILED(rc)) return rc;
3019
3020 i_setModified(IsModified_MachineData);
3021 mUserData.backup();
3022 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3023 return S_OK;
3024}
3025
3026HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3027{
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3031 return S_OK;
3032}
3033
3034HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3035{
3036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3037
3038 /* @todo deal with running state change. */
3039 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3040 if (FAILED(rc)) return rc;
3041
3042 i_setModified(IsModified_MachineData);
3043 mUserData.backup();
3044 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3045 return S_OK;
3046}
3047
3048HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3049{
3050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3051
3052 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3053
3054 return S_OK;
3055}
3056
3057HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3058{
3059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3060
3061 /* @todo deal with running state change. */
3062 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3063 if (FAILED(rc)) return rc;
3064
3065 i_setModified(IsModified_MachineData);
3066 mUserData.backup();
3067 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3068
3069 return S_OK;
3070}
3071
3072HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3073{
3074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3077 return S_OK;
3078}
3079
3080HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3081{
3082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 /* @todo deal with running state change. */
3085 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3086 if (FAILED(rc)) return rc;
3087
3088 i_setModified(IsModified_MachineData);
3089 mUserData.backup();
3090 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3091 return S_OK;
3092}
3093
3094HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3095{
3096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3097
3098 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3099
3100 return S_OK;
3101}
3102
3103HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
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 ( aRTCUseUTC
3110 && mData->mRegistered
3111 && ( !i_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 i_setModified(IsModified_MachineData);
3123 mUserData.backup();
3124 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3125
3126 return S_OK;
3127}
3128
3129HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3130{
3131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3132
3133 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3134
3135 return S_OK;
3136}
3137
3138HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3139{
3140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3141
3142 HRESULT rc = i_checkStateDependency(MutableStateDep);
3143 if (FAILED(rc)) return rc;
3144
3145 i_setModified(IsModified_MachineData);
3146 mHWData.backup();
3147 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3148
3149 return S_OK;
3150}
3151
3152HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3153{
3154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3155
3156 *aIOCacheSize = mHWData->mIOCacheSize;
3157
3158 return S_OK;
3159}
3160
3161HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3162{
3163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3164
3165 HRESULT rc = i_checkStateDependency(MutableStateDep);
3166 if (FAILED(rc)) return rc;
3167
3168 i_setModified(IsModified_MachineData);
3169 mHWData.backup();
3170 mHWData->mIOCacheSize = aIOCacheSize;
3171
3172 return S_OK;
3173}
3174
3175
3176/**
3177 * @note Locks objects!
3178 */
3179HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3180 LockType_T aLockType)
3181{
3182 /* check the session state */
3183 SessionState_T state;
3184 HRESULT rc = aSession->COMGETTER(State)(&state);
3185 if (FAILED(rc)) return rc;
3186
3187 if (state != SessionState_Unlocked)
3188 return setError(VBOX_E_INVALID_OBJECT_STATE,
3189 tr("The given session is busy"));
3190
3191 // get the client's IInternalSessionControl interface
3192 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3193 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3194 E_INVALIDARG);
3195
3196 // session name (only used in some code paths)
3197 Utf8Str strSessionName;
3198
3199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 if (!mData->mRegistered)
3202 return setError(E_UNEXPECTED,
3203 tr("The machine '%s' is not registered"),
3204 mUserData->s.strName.c_str());
3205
3206 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3207
3208 SessionState_T oldState = mData->mSession.mState;
3209 /* Hack: in case the session is closing and there is a progress object
3210 * which allows waiting for the session to be closed, take the opportunity
3211 * and do a limited wait (max. 1 second). This helps a lot when the system
3212 * is busy and thus session closing can take a little while. */
3213 if ( mData->mSession.mState == SessionState_Unlocking
3214 && mData->mSession.mProgress)
3215 {
3216 alock.release();
3217 mData->mSession.mProgress->WaitForCompletion(1000);
3218 alock.acquire();
3219 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3220 }
3221
3222 // try again now
3223 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3224 // (i.e. session machine exists)
3225 && (aLockType == LockType_Shared) // caller wants a shared link to the
3226 // existing session that holds the write lock:
3227 )
3228 {
3229 // OK, share the session... we are now dealing with three processes:
3230 // 1) VBoxSVC (where this code runs);
3231 // 2) process C: the caller's client process (who wants a shared session);
3232 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3233
3234 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3235 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3236 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3237 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3238 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3239
3240 /*
3241 * Release the lock before calling the client process. It's safe here
3242 * since the only thing to do after we get the lock again is to add
3243 * the remote control to the list (which doesn't directly influence
3244 * anything).
3245 */
3246 alock.release();
3247
3248 // get the console of the session holding the write lock (this is a remote call)
3249 ComPtr<IConsole> pConsoleW;
3250 if (mData->mSession.mLockType == LockType_VM)
3251 {
3252 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3253 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3254 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3255 if (FAILED(rc))
3256 // the failure may occur w/o any error info (from RPC), so provide one
3257 return setError(VBOX_E_VM_ERROR,
3258 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3259 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3260 }
3261
3262 // share the session machine and W's console with the caller's session
3263 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3264 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3265 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3266
3267 if (FAILED(rc))
3268 // the failure may occur w/o any error info (from RPC), so provide one
3269 return setError(VBOX_E_VM_ERROR,
3270 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3271 alock.acquire();
3272
3273 // need to revalidate the state after acquiring the lock again
3274 if (mData->mSession.mState != SessionState_Locked)
3275 {
3276 pSessionControl->Uninitialize();
3277 return setError(VBOX_E_INVALID_SESSION_STATE,
3278 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3279 mUserData->s.strName.c_str());
3280 }
3281
3282 // add the caller's session to the list
3283 mData->mSession.mRemoteControls.push_back(pSessionControl);
3284 }
3285 else if ( mData->mSession.mState == SessionState_Locked
3286 || mData->mSession.mState == SessionState_Unlocking
3287 )
3288 {
3289 // sharing not permitted, or machine still unlocking:
3290 return setError(VBOX_E_INVALID_OBJECT_STATE,
3291 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3292 mUserData->s.strName.c_str());
3293 }
3294 else
3295 {
3296 // machine is not locked: then write-lock the machine (create the session machine)
3297
3298 // must not be busy
3299 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3300
3301 // get the caller's session PID
3302 RTPROCESS pid = NIL_RTPROCESS;
3303 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3304 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3305 Assert(pid != NIL_RTPROCESS);
3306
3307 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3308
3309 if (fLaunchingVMProcess)
3310 {
3311 if (mData->mSession.mPID == NIL_RTPROCESS)
3312 {
3313 // two or more clients racing for a lock, the one which set the
3314 // session state to Spawning will win, the others will get an
3315 // error as we can't decide here if waiting a little would help
3316 // (only for shared locks this would avoid an error)
3317 return setError(VBOX_E_INVALID_OBJECT_STATE,
3318 tr("The machine '%s' already has a lock request pending"),
3319 mUserData->s.strName.c_str());
3320 }
3321
3322 // this machine is awaiting for a spawning session to be opened:
3323 // then the calling process must be the one that got started by
3324 // LaunchVMProcess()
3325
3326 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3327 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3328
3329#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3330 /* Hardened windows builds spawns three processes when a VM is
3331 launched, the 3rd one is the one that will end up here. */
3332 RTPROCESS ppid;
3333 int rc = RTProcQueryParent(pid, &ppid);
3334 if (RT_SUCCESS(rc))
3335 rc = RTProcQueryParent(ppid, &ppid);
3336 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3337 || rc == VERR_ACCESS_DENIED)
3338 {
3339 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3340 mData->mSession.mPID = pid;
3341 }
3342#endif
3343
3344 if (mData->mSession.mPID != pid)
3345 return setError(E_ACCESSDENIED,
3346 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3347 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3348 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3349 }
3350
3351 // create the mutable SessionMachine from the current machine
3352 ComObjPtr<SessionMachine> sessionMachine;
3353 sessionMachine.createObject();
3354 rc = sessionMachine->init(this);
3355 AssertComRC(rc);
3356
3357 /* NOTE: doing return from this function after this point but
3358 * before the end is forbidden since it may call SessionMachine::uninit()
3359 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3360 * lock while still holding the Machine lock in alock so that a deadlock
3361 * is possible due to the wrong lock order. */
3362
3363 if (SUCCEEDED(rc))
3364 {
3365 /*
3366 * Set the session state to Spawning to protect against subsequent
3367 * attempts to open a session and to unregister the machine after
3368 * we release the lock.
3369 */
3370 SessionState_T origState = mData->mSession.mState;
3371 mData->mSession.mState = SessionState_Spawning;
3372
3373#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3374 /* Get the client token ID to be passed to the client process */
3375 Utf8Str strTokenId;
3376 sessionMachine->i_getTokenId(strTokenId);
3377 Assert(!strTokenId.isEmpty());
3378#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3379 /* Get the client token to be passed to the client process */
3380 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3381 /* The token is now "owned" by pToken, fix refcount */
3382 if (!pToken.isNull())
3383 pToken->Release();
3384#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3385
3386 /*
3387 * Release the lock before calling the client process -- it will call
3388 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3389 * because the state is Spawning, so that LaunchVMProcess() and
3390 * LockMachine() calls will fail. This method, called before we
3391 * acquire the lock again, will fail because of the wrong PID.
3392 *
3393 * Note that mData->mSession.mRemoteControls accessed outside
3394 * the lock may not be modified when state is Spawning, so it's safe.
3395 */
3396 alock.release();
3397
3398 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3399#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3400 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3401#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3402 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3403 /* Now the token is owned by the client process. */
3404 pToken.setNull();
3405#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3406 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3407
3408 /* The failure may occur w/o any error info (from RPC), so provide one */
3409 if (FAILED(rc))
3410 setError(VBOX_E_VM_ERROR,
3411 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3412
3413 // get session name, either to remember or to compare against
3414 // the already known session name.
3415 {
3416 Bstr bstrSessionName;
3417 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3418 if (SUCCEEDED(rc2))
3419 strSessionName = bstrSessionName;
3420 }
3421
3422 if ( SUCCEEDED(rc)
3423 && fLaunchingVMProcess
3424 )
3425 {
3426 /* complete the remote session initialization */
3427
3428 /* get the console from the direct session */
3429 ComPtr<IConsole> console;
3430 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3431 ComAssertComRC(rc);
3432
3433 if (SUCCEEDED(rc) && !console)
3434 {
3435 ComAssert(!!console);
3436 rc = E_FAIL;
3437 }
3438
3439 /* assign machine & console to the remote session */
3440 if (SUCCEEDED(rc))
3441 {
3442 /*
3443 * after LaunchVMProcess(), the first and the only
3444 * entry in remoteControls is that remote session
3445 */
3446 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3447 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3448 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3449
3450 /* The failure may occur w/o any error info (from RPC), so provide one */
3451 if (FAILED(rc))
3452 setError(VBOX_E_VM_ERROR,
3453 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3454 }
3455
3456 if (FAILED(rc))
3457 pSessionControl->Uninitialize();
3458 }
3459
3460 /* acquire the lock again */
3461 alock.acquire();
3462
3463 /* Restore the session state */
3464 mData->mSession.mState = origState;
3465 }
3466
3467 // finalize spawning anyway (this is why we don't return on errors above)
3468 if (fLaunchingVMProcess)
3469 {
3470 Assert(mData->mSession.mName == strSessionName);
3471 /* Note that the progress object is finalized later */
3472 /** @todo Consider checking mData->mSession.mProgress for cancellation
3473 * around here. */
3474
3475 /* We don't reset mSession.mPID here because it is necessary for
3476 * SessionMachine::uninit() to reap the child process later. */
3477
3478 if (FAILED(rc))
3479 {
3480 /* Close the remote session, remove the remote control from the list
3481 * and reset session state to Closed (@note keep the code in sync
3482 * with the relevant part in checkForSpawnFailure()). */
3483
3484 Assert(mData->mSession.mRemoteControls.size() == 1);
3485 if (mData->mSession.mRemoteControls.size() == 1)
3486 {
3487 ErrorInfoKeeper eik;
3488 mData->mSession.mRemoteControls.front()->Uninitialize();
3489 }
3490
3491 mData->mSession.mRemoteControls.clear();
3492 mData->mSession.mState = SessionState_Unlocked;
3493 }
3494 }
3495 else
3496 {
3497 /* memorize PID of the directly opened session */
3498 if (SUCCEEDED(rc))
3499 mData->mSession.mPID = pid;
3500 }
3501
3502 if (SUCCEEDED(rc))
3503 {
3504 mData->mSession.mLockType = aLockType;
3505 /* memorize the direct session control and cache IUnknown for it */
3506 mData->mSession.mDirectControl = pSessionControl;
3507 mData->mSession.mState = SessionState_Locked;
3508 if (!fLaunchingVMProcess)
3509 mData->mSession.mName = strSessionName;
3510 /* associate the SessionMachine with this Machine */
3511 mData->mSession.mMachine = sessionMachine;
3512
3513 /* request an IUnknown pointer early from the remote party for later
3514 * identity checks (it will be internally cached within mDirectControl
3515 * at least on XPCOM) */
3516 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3517 NOREF(unk);
3518 }
3519
3520 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3521 * would break the lock order */
3522 alock.release();
3523
3524 /* uninitialize the created session machine on failure */
3525 if (FAILED(rc))
3526 sessionMachine->uninit();
3527 }
3528
3529 if (SUCCEEDED(rc))
3530 {
3531 /*
3532 * tell the client watcher thread to update the set of
3533 * machines that have open sessions
3534 */
3535 mParent->i_updateClientWatcher();
3536
3537 if (oldState != SessionState_Locked)
3538 /* fire an event */
3539 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3540 }
3541
3542 return rc;
3543}
3544
3545/**
3546 * @note Locks objects!
3547 */
3548HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3549 const com::Utf8Str &aName,
3550 const com::Utf8Str &aEnvironment,
3551 ComPtr<IProgress> &aProgress)
3552{
3553 Utf8Str strFrontend(aName);
3554 /* "emergencystop" doesn't need the session, so skip the checks/interface
3555 * retrieval. This code doesn't quite fit in here, but introducing a
3556 * special API method would be even more effort, and would require explicit
3557 * support by every API client. It's better to hide the feature a bit. */
3558 if (strFrontend != "emergencystop")
3559 CheckComArgNotNull(aSession);
3560
3561 HRESULT rc = S_OK;
3562 if (strFrontend.isEmpty())
3563 {
3564 Bstr bstrFrontend;
3565 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3566 if (FAILED(rc))
3567 return rc;
3568 strFrontend = bstrFrontend;
3569 if (strFrontend.isEmpty())
3570 {
3571 ComPtr<ISystemProperties> systemProperties;
3572 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3573 if (FAILED(rc))
3574 return rc;
3575 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3576 if (FAILED(rc))
3577 return rc;
3578 strFrontend = bstrFrontend;
3579 }
3580 /* paranoia - emergencystop is not a valid default */
3581 if (strFrontend == "emergencystop")
3582 strFrontend = Utf8Str::Empty;
3583 }
3584 /* default frontend: Qt GUI */
3585 if (strFrontend.isEmpty())
3586 strFrontend = "GUI/Qt";
3587
3588 if (strFrontend != "emergencystop")
3589 {
3590 /* check the session state */
3591 SessionState_T state;
3592 rc = aSession->COMGETTER(State)(&state);
3593 if (FAILED(rc))
3594 return rc;
3595
3596 if (state != SessionState_Unlocked)
3597 return setError(VBOX_E_INVALID_OBJECT_STATE,
3598 tr("The given session is busy"));
3599
3600 /* get the IInternalSessionControl interface */
3601 ComPtr<IInternalSessionControl> control(aSession);
3602 ComAssertMsgRet(!control.isNull(),
3603 ("No IInternalSessionControl interface"),
3604 E_INVALIDARG);
3605
3606 /* get the teleporter enable state for the progress object init. */
3607 BOOL fTeleporterEnabled;
3608 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3609 if (FAILED(rc))
3610 return rc;
3611
3612 /* create a progress object */
3613 ComObjPtr<ProgressProxy> progress;
3614 progress.createObject();
3615 rc = progress->init(mParent,
3616 static_cast<IMachine*>(this),
3617 Bstr(tr("Starting VM")).raw(),
3618 TRUE /* aCancelable */,
3619 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3620 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3621 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3622 2 /* uFirstOperationWeight */,
3623 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3624
3625 if (SUCCEEDED(rc))
3626 {
3627 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3628 if (SUCCEEDED(rc))
3629 {
3630 aProgress = progress;
3631
3632 /* signal the client watcher thread */
3633 mParent->i_updateClientWatcher();
3634
3635 /* fire an event */
3636 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3637 }
3638 }
3639 }
3640 else
3641 {
3642 /* no progress object - either instant success or failure */
3643 aProgress = NULL;
3644
3645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3646
3647 if (mData->mSession.mState != SessionState_Locked)
3648 return setError(VBOX_E_INVALID_OBJECT_STATE,
3649 tr("The machine '%s' is not locked by a session"),
3650 mUserData->s.strName.c_str());
3651
3652 /* must have a VM process associated - do not kill normal API clients
3653 * with an open session */
3654 if (!Global::IsOnline(mData->mMachineState))
3655 return setError(VBOX_E_INVALID_OBJECT_STATE,
3656 tr("The machine '%s' does not have a VM process"),
3657 mUserData->s.strName.c_str());
3658
3659 /* forcibly terminate the VM process */
3660 if (mData->mSession.mPID != NIL_RTPROCESS)
3661 RTProcTerminate(mData->mSession.mPID);
3662
3663 /* signal the client watcher thread, as most likely the client has
3664 * been terminated */
3665 mParent->i_updateClientWatcher();
3666 }
3667
3668 return rc;
3669}
3670
3671HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3672{
3673 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3674 return setError(E_INVALIDARG,
3675 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3676 aPosition, SchemaDefs::MaxBootPosition);
3677
3678 if (aDevice == DeviceType_USB)
3679 return setError(E_NOTIMPL,
3680 tr("Booting from USB device is currently not supported"));
3681
3682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3683
3684 HRESULT rc = i_checkStateDependency(MutableStateDep);
3685 if (FAILED(rc)) return rc;
3686
3687 i_setModified(IsModified_MachineData);
3688 mHWData.backup();
3689 mHWData->mBootOrder[aPosition - 1] = aDevice;
3690
3691 return S_OK;
3692}
3693
3694HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3695{
3696 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3697 return setError(E_INVALIDARG,
3698 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3699 aPosition, SchemaDefs::MaxBootPosition);
3700
3701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3702
3703 *aDevice = mHWData->mBootOrder[aPosition - 1];
3704
3705 return S_OK;
3706}
3707
3708HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3709 LONG aControllerPort,
3710 LONG aDevice,
3711 DeviceType_T aType,
3712 const ComPtr<IMedium> &aMedium)
3713{
3714 IMedium *aM = aMedium;
3715 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3716 aName.c_str(), aControllerPort, aDevice, aType, aM));
3717
3718 // request the host lock first, since might be calling Host methods for getting host drives;
3719 // next, protect the media tree all the while we're in here, as well as our member variables
3720 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3721 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3722
3723 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3724 if (FAILED(rc)) return rc;
3725
3726 /// @todo NEWMEDIA implicit machine registration
3727 if (!mData->mRegistered)
3728 return setError(VBOX_E_INVALID_OBJECT_STATE,
3729 tr("Cannot attach storage devices to an unregistered machine"));
3730
3731 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3732
3733 /* Check for an existing controller. */
3734 ComObjPtr<StorageController> ctl;
3735 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3736 if (FAILED(rc)) return rc;
3737
3738 StorageControllerType_T ctrlType;
3739 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3740 if (FAILED(rc))
3741 return setError(E_FAIL,
3742 tr("Could not get type of controller '%s'"),
3743 aName.c_str());
3744
3745 bool fSilent = false;
3746 Utf8Str strReconfig;
3747
3748 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3749 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3750 if ( mData->mMachineState == MachineState_Paused
3751 && strReconfig == "1")
3752 fSilent = true;
3753
3754 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3755 bool fHotplug = false;
3756 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3757 fHotplug = true;
3758
3759 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3760 return setError(VBOX_E_INVALID_VM_STATE,
3761 tr("Controller '%s' does not support hotplugging"),
3762 aName.c_str());
3763
3764 // check that the port and device are not out of range
3765 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3766 if (FAILED(rc)) return rc;
3767
3768 /* check if the device slot is already busy */
3769 MediumAttachment *pAttachTemp;
3770 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3771 Bstr(aName).raw(),
3772 aControllerPort,
3773 aDevice)))
3774 {
3775 Medium *pMedium = pAttachTemp->i_getMedium();
3776 if (pMedium)
3777 {
3778 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3779 return setError(VBOX_E_OBJECT_IN_USE,
3780 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3781 pMedium->i_getLocationFull().c_str(),
3782 aControllerPort,
3783 aDevice,
3784 aName.c_str());
3785 }
3786 else
3787 return setError(VBOX_E_OBJECT_IN_USE,
3788 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3789 aControllerPort, aDevice, aName.c_str());
3790 }
3791
3792 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3793 if (aMedium && medium.isNull())
3794 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3795
3796 AutoCaller mediumCaller(medium);
3797 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3798
3799 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3800
3801 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3802 && !medium.isNull()
3803 )
3804 return setError(VBOX_E_OBJECT_IN_USE,
3805 tr("Medium '%s' is already attached to this virtual machine"),
3806 medium->i_getLocationFull().c_str());
3807
3808 if (!medium.isNull())
3809 {
3810 MediumType_T mtype = medium->i_getType();
3811 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3812 // For DVDs it's not written to the config file, so needs no global config
3813 // version bump. For floppies it's a new attribute "type", which is ignored
3814 // by older VirtualBox version, so needs no global config version bump either.
3815 // For hard disks this type is not accepted.
3816 if (mtype == MediumType_MultiAttach)
3817 {
3818 // This type is new with VirtualBox 4.0 and therefore requires settings
3819 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3820 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3821 // two reasons: The medium type is a property of the media registry tree, which
3822 // can reside in the global config file (for pre-4.0 media); we would therefore
3823 // possibly need to bump the global config version. We don't want to do that though
3824 // because that might make downgrading to pre-4.0 impossible.
3825 // As a result, we can only use these two new types if the medium is NOT in the
3826 // global registry:
3827 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3828 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3829 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3830 )
3831 return setError(VBOX_E_INVALID_OBJECT_STATE,
3832 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3833 "to machines that were created with VirtualBox 4.0 or later"),
3834 medium->i_getLocationFull().c_str());
3835 }
3836 }
3837
3838 bool fIndirect = false;
3839 if (!medium.isNull())
3840 fIndirect = medium->i_isReadOnly();
3841 bool associate = true;
3842
3843 do
3844 {
3845 if ( aType == DeviceType_HardDisk
3846 && mMediaData.isBackedUp())
3847 {
3848 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3849
3850 /* check if the medium was attached to the VM before we started
3851 * changing attachments in which case the attachment just needs to
3852 * be restored */
3853 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3854 {
3855 AssertReturn(!fIndirect, E_FAIL);
3856
3857 /* see if it's the same bus/channel/device */
3858 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3859 {
3860 /* the simplest case: restore the whole attachment
3861 * and return, nothing else to do */
3862 mMediaData->mAttachments.push_back(pAttachTemp);
3863
3864 /* Reattach the medium to the VM. */
3865 if (fHotplug || fSilent)
3866 {
3867 mediumLock.release();
3868 treeLock.release();
3869 alock.release();
3870
3871 MediumLockList *pMediumLockList(new MediumLockList());
3872
3873 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3874 true /* fMediumLockWrite */,
3875 false /* fMediumLockWriteAll */,
3876 NULL,
3877 *pMediumLockList);
3878 alock.acquire();
3879 if (FAILED(rc))
3880 delete pMediumLockList;
3881 else
3882 {
3883 mData->mSession.mLockedMedia.Unlock();
3884 alock.release();
3885 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3886 mData->mSession.mLockedMedia.Lock();
3887 alock.acquire();
3888 }
3889 alock.release();
3890
3891 if (SUCCEEDED(rc))
3892 {
3893 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3894 /* Remove lock list in case of error. */
3895 if (FAILED(rc))
3896 {
3897 mData->mSession.mLockedMedia.Unlock();
3898 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3899 mData->mSession.mLockedMedia.Lock();
3900 }
3901 }
3902 }
3903
3904 return S_OK;
3905 }
3906
3907 /* bus/channel/device differ; we need a new attachment object,
3908 * but don't try to associate it again */
3909 associate = false;
3910 break;
3911 }
3912 }
3913
3914 /* go further only if the attachment is to be indirect */
3915 if (!fIndirect)
3916 break;
3917
3918 /* perform the so called smart attachment logic for indirect
3919 * attachments. Note that smart attachment is only applicable to base
3920 * hard disks. */
3921
3922 if (medium->i_getParent().isNull())
3923 {
3924 /* first, investigate the backup copy of the current hard disk
3925 * attachments to make it possible to re-attach existing diffs to
3926 * another device slot w/o losing their contents */
3927 if (mMediaData.isBackedUp())
3928 {
3929 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3930
3931 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3932 uint32_t foundLevel = 0;
3933
3934 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3935 {
3936 uint32_t level = 0;
3937 MediumAttachment *pAttach = *it;
3938 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3939 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3940 if (pMedium.isNull())
3941 continue;
3942
3943 if (pMedium->i_getBase(&level) == medium)
3944 {
3945 /* skip the hard disk if its currently attached (we
3946 * cannot attach the same hard disk twice) */
3947 if (i_findAttachment(mMediaData->mAttachments,
3948 pMedium))
3949 continue;
3950
3951 /* matched device, channel and bus (i.e. attached to the
3952 * same place) will win and immediately stop the search;
3953 * otherwise the attachment that has the youngest
3954 * descendant of medium will be used
3955 */
3956 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3957 {
3958 /* the simplest case: restore the whole attachment
3959 * and return, nothing else to do */
3960 mMediaData->mAttachments.push_back(*it);
3961
3962 /* Reattach the medium to the VM. */
3963 if (fHotplug || fSilent)
3964 {
3965 mediumLock.release();
3966 treeLock.release();
3967 alock.release();
3968
3969 MediumLockList *pMediumLockList(new MediumLockList());
3970
3971 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3972 true /* fMediumLockWrite */,
3973 false /* fMediumLockWriteAll */,
3974 NULL,
3975 *pMediumLockList);
3976 alock.acquire();
3977 if (FAILED(rc))
3978 delete pMediumLockList;
3979 else
3980 {
3981 mData->mSession.mLockedMedia.Unlock();
3982 alock.release();
3983 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3984 mData->mSession.mLockedMedia.Lock();
3985 alock.acquire();
3986 }
3987 alock.release();
3988
3989 if (SUCCEEDED(rc))
3990 {
3991 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3992 /* Remove lock list in case of error. */
3993 if (FAILED(rc))
3994 {
3995 mData->mSession.mLockedMedia.Unlock();
3996 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3997 mData->mSession.mLockedMedia.Lock();
3998 }
3999 }
4000 }
4001
4002 return S_OK;
4003 }
4004 else if ( foundIt == oldAtts.end()
4005 || level > foundLevel /* prefer younger */
4006 )
4007 {
4008 foundIt = it;
4009 foundLevel = level;
4010 }
4011 }
4012 }
4013
4014 if (foundIt != oldAtts.end())
4015 {
4016 /* use the previously attached hard disk */
4017 medium = (*foundIt)->i_getMedium();
4018 mediumCaller.attach(medium);
4019 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4020 mediumLock.attach(medium);
4021 /* not implicit, doesn't require association with this VM */
4022 fIndirect = false;
4023 associate = false;
4024 /* go right to the MediumAttachment creation */
4025 break;
4026 }
4027 }
4028
4029 /* must give up the medium lock and medium tree lock as below we
4030 * go over snapshots, which needs a lock with higher lock order. */
4031 mediumLock.release();
4032 treeLock.release();
4033
4034 /* then, search through snapshots for the best diff in the given
4035 * hard disk's chain to base the new diff on */
4036
4037 ComObjPtr<Medium> base;
4038 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4039 while (snap)
4040 {
4041 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4042
4043 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4044
4045 MediumAttachment *pAttachFound = NULL;
4046 uint32_t foundLevel = 0;
4047
4048 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4049 {
4050 MediumAttachment *pAttach = *it;
4051 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4052 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4053 if (pMedium.isNull())
4054 continue;
4055
4056 uint32_t level = 0;
4057 if (pMedium->i_getBase(&level) == medium)
4058 {
4059 /* matched device, channel and bus (i.e. attached to the
4060 * same place) will win and immediately stop the search;
4061 * otherwise the attachment that has the youngest
4062 * descendant of medium will be used
4063 */
4064 if ( pAttach->i_getDevice() == aDevice
4065 && pAttach->i_getPort() == aControllerPort
4066 && pAttach->i_getControllerName() == aName
4067 )
4068 {
4069 pAttachFound = pAttach;
4070 break;
4071 }
4072 else if ( !pAttachFound
4073 || level > foundLevel /* prefer younger */
4074 )
4075 {
4076 pAttachFound = pAttach;
4077 foundLevel = level;
4078 }
4079 }
4080 }
4081
4082 if (pAttachFound)
4083 {
4084 base = pAttachFound->i_getMedium();
4085 break;
4086 }
4087
4088 snap = snap->i_getParent();
4089 }
4090
4091 /* re-lock medium tree and the medium, as we need it below */
4092 treeLock.acquire();
4093 mediumLock.acquire();
4094
4095 /* found a suitable diff, use it as a base */
4096 if (!base.isNull())
4097 {
4098 medium = base;
4099 mediumCaller.attach(medium);
4100 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4101 mediumLock.attach(medium);
4102 }
4103 }
4104
4105 Utf8Str strFullSnapshotFolder;
4106 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4107
4108 ComObjPtr<Medium> diff;
4109 diff.createObject();
4110 // store this diff in the same registry as the parent
4111 Guid uuidRegistryParent;
4112 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4113 {
4114 // parent image has no registry: this can happen if we're attaching a new immutable
4115 // image that has not yet been attached (medium then points to the base and we're
4116 // creating the diff image for the immutable, and the parent is not yet registered);
4117 // put the parent in the machine registry then
4118 mediumLock.release();
4119 treeLock.release();
4120 alock.release();
4121 i_addMediumToRegistry(medium);
4122 alock.acquire();
4123 treeLock.acquire();
4124 mediumLock.acquire();
4125 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4126 }
4127 rc = diff->init(mParent,
4128 medium->i_getPreferredDiffFormat(),
4129 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4130 uuidRegistryParent,
4131 DeviceType_HardDisk);
4132 if (FAILED(rc)) return rc;
4133
4134 /* Apply the normal locking logic to the entire chain. */
4135 MediumLockList *pMediumLockList(new MediumLockList());
4136 mediumLock.release();
4137 treeLock.release();
4138 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4139 true /* fMediumLockWrite */,
4140 false /* fMediumLockWriteAll */,
4141 medium,
4142 *pMediumLockList);
4143 treeLock.acquire();
4144 mediumLock.acquire();
4145 if (SUCCEEDED(rc))
4146 {
4147 mediumLock.release();
4148 treeLock.release();
4149 rc = pMediumLockList->Lock();
4150 treeLock.acquire();
4151 mediumLock.acquire();
4152 if (FAILED(rc))
4153 setError(rc,
4154 tr("Could not lock medium when creating diff '%s'"),
4155 diff->i_getLocationFull().c_str());
4156 else
4157 {
4158 /* will release the lock before the potentially lengthy
4159 * operation, so protect with the special state */
4160 MachineState_T oldState = mData->mMachineState;
4161 i_setMachineState(MachineState_SettingUp);
4162
4163 mediumLock.release();
4164 treeLock.release();
4165 alock.release();
4166
4167 rc = medium->i_createDiffStorage(diff,
4168 MediumVariant_Standard,
4169 pMediumLockList,
4170 NULL /* aProgress */,
4171 true /* aWait */);
4172
4173 alock.acquire();
4174 treeLock.acquire();
4175 mediumLock.acquire();
4176
4177 i_setMachineState(oldState);
4178 }
4179 }
4180
4181 /* Unlock the media and free the associated memory. */
4182 delete pMediumLockList;
4183
4184 if (FAILED(rc)) return rc;
4185
4186 /* use the created diff for the actual attachment */
4187 medium = diff;
4188 mediumCaller.attach(medium);
4189 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4190 mediumLock.attach(medium);
4191 }
4192 while (0);
4193
4194 ComObjPtr<MediumAttachment> attachment;
4195 attachment.createObject();
4196 rc = attachment->init(this,
4197 medium,
4198 aName,
4199 aControllerPort,
4200 aDevice,
4201 aType,
4202 fIndirect,
4203 false /* fPassthrough */,
4204 false /* fTempEject */,
4205 false /* fNonRotational */,
4206 false /* fDiscard */,
4207 fHotplug /* fHotPluggable */,
4208 Utf8Str::Empty);
4209 if (FAILED(rc)) return rc;
4210
4211 if (associate && !medium.isNull())
4212 {
4213 // as the last step, associate the medium to the VM
4214 rc = medium->i_addBackReference(mData->mUuid);
4215 // here we can fail because of Deleting, or being in process of creating a Diff
4216 if (FAILED(rc)) return rc;
4217
4218 mediumLock.release();
4219 treeLock.release();
4220 alock.release();
4221 i_addMediumToRegistry(medium);
4222 alock.acquire();
4223 treeLock.acquire();
4224 mediumLock.acquire();
4225 }
4226
4227 /* success: finally remember the attachment */
4228 i_setModified(IsModified_Storage);
4229 mMediaData.backup();
4230 mMediaData->mAttachments.push_back(attachment);
4231
4232 mediumLock.release();
4233 treeLock.release();
4234 alock.release();
4235
4236 if (fHotplug || fSilent)
4237 {
4238 if (!medium.isNull())
4239 {
4240 MediumLockList *pMediumLockList(new MediumLockList());
4241
4242 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4243 true /* fMediumLockWrite */,
4244 false /* fMediumLockWriteAll */,
4245 NULL,
4246 *pMediumLockList);
4247 alock.acquire();
4248 if (FAILED(rc))
4249 delete pMediumLockList;
4250 else
4251 {
4252 mData->mSession.mLockedMedia.Unlock();
4253 alock.release();
4254 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4255 mData->mSession.mLockedMedia.Lock();
4256 alock.acquire();
4257 }
4258 alock.release();
4259 }
4260
4261 if (SUCCEEDED(rc))
4262 {
4263 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4264 /* Remove lock list in case of error. */
4265 if (FAILED(rc))
4266 {
4267 mData->mSession.mLockedMedia.Unlock();
4268 mData->mSession.mLockedMedia.Remove(attachment);
4269 mData->mSession.mLockedMedia.Lock();
4270 }
4271 }
4272 }
4273
4274 /* Save modified registries, but skip this machine as it's the caller's
4275 * job to save its settings like all other settings changes. */
4276 mParent->i_unmarkRegistryModified(i_getId());
4277 mParent->i_saveModifiedRegistries();
4278
4279 return rc;
4280}
4281
4282HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4283 LONG aDevice)
4284{
4285 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4286 aName.c_str(), aControllerPort, aDevice));
4287
4288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4289
4290 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4291 if (FAILED(rc)) return rc;
4292
4293 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4294
4295 /* Check for an existing controller. */
4296 ComObjPtr<StorageController> ctl;
4297 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4298 if (FAILED(rc)) return rc;
4299
4300 StorageControllerType_T ctrlType;
4301 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4302 if (FAILED(rc))
4303 return setError(E_FAIL,
4304 tr("Could not get type of controller '%s'"),
4305 aName.c_str());
4306
4307 bool fSilent = false;
4308 Utf8Str strReconfig;
4309
4310 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4311 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4312 if ( mData->mMachineState == MachineState_Paused
4313 && strReconfig == "1")
4314 fSilent = true;
4315
4316 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4317 bool fHotplug = false;
4318 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4319 fHotplug = true;
4320
4321 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4322 return setError(VBOX_E_INVALID_VM_STATE,
4323 tr("Controller '%s' does not support hotplugging"),
4324 aName.c_str());
4325
4326 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4327 Bstr(aName).raw(),
4328 aControllerPort,
4329 aDevice);
4330 if (!pAttach)
4331 return setError(VBOX_E_OBJECT_NOT_FOUND,
4332 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4333 aDevice, aControllerPort, aName.c_str());
4334
4335 if (fHotplug && !pAttach->i_getHotPluggable())
4336 return setError(VBOX_E_NOT_SUPPORTED,
4337 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4338 aDevice, aControllerPort, aName.c_str());
4339
4340 /*
4341 * The VM has to detach the device before we delete any implicit diffs.
4342 * If this fails we can roll back without loosing data.
4343 */
4344 if (fHotplug || fSilent)
4345 {
4346 alock.release();
4347 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4348 alock.acquire();
4349 }
4350 if (FAILED(rc)) return rc;
4351
4352 /* If we are here everything went well and we can delete the implicit now. */
4353 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4354
4355 alock.release();
4356
4357 /* Save modified registries, but skip this machine as it's the caller's
4358 * job to save its settings like all other settings changes. */
4359 mParent->i_unmarkRegistryModified(i_getId());
4360 mParent->i_saveModifiedRegistries();
4361
4362 return rc;
4363}
4364
4365HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4366 LONG aDevice, BOOL aPassthrough)
4367{
4368 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4369 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4370
4371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4372
4373 HRESULT rc = i_checkStateDependency(MutableStateDep);
4374 if (FAILED(rc)) return rc;
4375
4376 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4377
4378 if (Global::IsOnlineOrTransient(mData->mMachineState))
4379 return setError(VBOX_E_INVALID_VM_STATE,
4380 tr("Invalid machine state: %s"),
4381 Global::stringifyMachineState(mData->mMachineState));
4382
4383 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4384 Bstr(aName).raw(),
4385 aControllerPort,
4386 aDevice);
4387 if (!pAttach)
4388 return setError(VBOX_E_OBJECT_NOT_FOUND,
4389 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4390 aDevice, aControllerPort, aName.c_str());
4391
4392
4393 i_setModified(IsModified_Storage);
4394 mMediaData.backup();
4395
4396 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4397
4398 if (pAttach->i_getType() != DeviceType_DVD)
4399 return setError(E_INVALIDARG,
4400 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4401 aDevice, aControllerPort, aName.c_str());
4402 pAttach->i_updatePassthrough(!!aPassthrough);
4403
4404 return S_OK;
4405}
4406
4407HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4408 LONG aDevice, BOOL aTemporaryEject)
4409{
4410
4411 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4412 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4413
4414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4415
4416 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4417 if (FAILED(rc)) return rc;
4418
4419 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4420 Bstr(aName).raw(),
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4426 aDevice, aControllerPort, aName.c_str());
4427
4428
4429 i_setModified(IsModified_Storage);
4430 mMediaData.backup();
4431
4432 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4433
4434 if (pAttach->i_getType() != DeviceType_DVD)
4435 return setError(E_INVALIDARG,
4436 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4437 aDevice, aControllerPort, aName.c_str());
4438 pAttach->i_updateTempEject(!!aTemporaryEject);
4439
4440 return S_OK;
4441}
4442
4443HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4444 LONG aDevice, BOOL aNonRotational)
4445{
4446
4447 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4448 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4449
4450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4451
4452 HRESULT rc = i_checkStateDependency(MutableStateDep);
4453 if (FAILED(rc)) return rc;
4454
4455 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4456
4457 if (Global::IsOnlineOrTransient(mData->mMachineState))
4458 return setError(VBOX_E_INVALID_VM_STATE,
4459 tr("Invalid machine state: %s"),
4460 Global::stringifyMachineState(mData->mMachineState));
4461
4462 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4463 Bstr(aName).raw(),
4464 aControllerPort,
4465 aDevice);
4466 if (!pAttach)
4467 return setError(VBOX_E_OBJECT_NOT_FOUND,
4468 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471
4472 i_setModified(IsModified_Storage);
4473 mMediaData.backup();
4474
4475 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4476
4477 if (pAttach->i_getType() != DeviceType_HardDisk)
4478 return setError(E_INVALIDARG,
4479 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4480 aDevice, aControllerPort, aName.c_str());
4481 pAttach->i_updateNonRotational(!!aNonRotational);
4482
4483 return S_OK;
4484}
4485
4486HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4487 LONG aDevice, BOOL aDiscard)
4488{
4489
4490 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4491 aName.c_str(), aControllerPort, aDevice, aDiscard));
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = i_checkStateDependency(MutableStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4499
4500 if (Global::IsOnlineOrTransient(mData->mMachineState))
4501 return setError(VBOX_E_INVALID_VM_STATE,
4502 tr("Invalid machine state: %s"),
4503 Global::stringifyMachineState(mData->mMachineState));
4504
4505 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4506 Bstr(aName).raw(),
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4512 aDevice, aControllerPort, aName.c_str());
4513
4514
4515 i_setModified(IsModified_Storage);
4516 mMediaData.backup();
4517
4518 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4519
4520 if (pAttach->i_getType() != DeviceType_HardDisk)
4521 return setError(E_INVALIDARG,
4522 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4523 aDevice, aControllerPort, aName.c_str());
4524 pAttach->i_updateDiscard(!!aDiscard);
4525
4526 return S_OK;
4527}
4528
4529HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4530 LONG aDevice, BOOL aHotPluggable)
4531{
4532 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4533 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4534
4535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4536
4537 HRESULT rc = i_checkStateDependency(MutableStateDep);
4538 if (FAILED(rc)) return rc;
4539
4540 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4541
4542 if (Global::IsOnlineOrTransient(mData->mMachineState))
4543 return setError(VBOX_E_INVALID_VM_STATE,
4544 tr("Invalid machine state: %s"),
4545 Global::stringifyMachineState(mData->mMachineState));
4546
4547 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4548 Bstr(aName).raw(),
4549 aControllerPort,
4550 aDevice);
4551 if (!pAttach)
4552 return setError(VBOX_E_OBJECT_NOT_FOUND,
4553 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4554 aDevice, aControllerPort, aName.c_str());
4555
4556 /* Check for an existing controller. */
4557 ComObjPtr<StorageController> ctl;
4558 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4559 if (FAILED(rc)) return rc;
4560
4561 StorageControllerType_T ctrlType;
4562 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4563 if (FAILED(rc))
4564 return setError(E_FAIL,
4565 tr("Could not get type of controller '%s'"),
4566 aName.c_str());
4567
4568 if (!i_isControllerHotplugCapable(ctrlType))
4569 return setError(VBOX_E_NOT_SUPPORTED,
4570 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4571 aName.c_str());
4572
4573 i_setModified(IsModified_Storage);
4574 mMediaData.backup();
4575
4576 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4577
4578 if (pAttach->i_getType() == DeviceType_Floppy)
4579 return setError(E_INVALIDARG,
4580 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4581 aDevice, aControllerPort, aName.c_str());
4582 pAttach->i_updateHotPluggable(!!aHotPluggable);
4583
4584 return S_OK;
4585}
4586
4587HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4588 LONG aDevice)
4589{
4590 int rc = S_OK;
4591 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4592 aName.c_str(), aControllerPort, aDevice));
4593
4594 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4595
4596 return rc;
4597}
4598
4599HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4600 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4601{
4602 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4603 aName.c_str(), aControllerPort, aDevice));
4604
4605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4606
4607 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4608 if (FAILED(rc)) return rc;
4609
4610 if (Global::IsOnlineOrTransient(mData->mMachineState))
4611 return setError(VBOX_E_INVALID_VM_STATE,
4612 tr("Invalid machine state: %s"),
4613 Global::stringifyMachineState(mData->mMachineState));
4614
4615 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4616 Bstr(aName).raw(),
4617 aControllerPort,
4618 aDevice);
4619 if (!pAttach)
4620 return setError(VBOX_E_OBJECT_NOT_FOUND,
4621 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4622 aDevice, aControllerPort, aName.c_str());
4623
4624
4625 i_setModified(IsModified_Storage);
4626 mMediaData.backup();
4627
4628 IBandwidthGroup *iB = aBandwidthGroup;
4629 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4630 if (aBandwidthGroup && group.isNull())
4631 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4632
4633 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4634
4635 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4636 if (strBandwidthGroupOld.isNotEmpty())
4637 {
4638 /* Get the bandwidth group object and release it - this must not fail. */
4639 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4640 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4641 Assert(SUCCEEDED(rc));
4642
4643 pBandwidthGroupOld->i_release();
4644 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4645 }
4646
4647 if (!group.isNull())
4648 {
4649 group->i_reference();
4650 pAttach->i_updateBandwidthGroup(group->i_getName());
4651 }
4652
4653 return S_OK;
4654}
4655
4656HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4657 LONG aControllerPort,
4658 LONG aDevice,
4659 DeviceType_T aType)
4660{
4661 HRESULT rc = S_OK;
4662
4663 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4664 aName.c_str(), aControllerPort, aDevice, aType));
4665
4666 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4667
4668 return rc;
4669}
4670
4671
4672HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4673 LONG aControllerPort,
4674 LONG aDevice,
4675 BOOL aForce)
4676{
4677 int rc = S_OK;
4678 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4679 aName.c_str(), aControllerPort, aForce));
4680
4681 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4682
4683 return rc;
4684}
4685
4686HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4687 LONG aControllerPort,
4688 LONG aDevice,
4689 const ComPtr<IMedium> &aMedium,
4690 BOOL aForce)
4691{
4692 int rc = S_OK;
4693 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4694 aName.c_str(), aControllerPort, aDevice, aForce));
4695
4696 // request the host lock first, since might be calling Host methods for getting host drives;
4697 // next, protect the media tree all the while we're in here, as well as our member variables
4698 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4699 this->lockHandle(),
4700 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4701
4702 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4703 Bstr(aName).raw(),
4704 aControllerPort,
4705 aDevice);
4706 if (pAttach.isNull())
4707 return setError(VBOX_E_OBJECT_NOT_FOUND,
4708 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4709 aDevice, aControllerPort, aName.c_str());
4710
4711 /* Remember previously mounted medium. The medium before taking the
4712 * backup is not necessarily the same thing. */
4713 ComObjPtr<Medium> oldmedium;
4714 oldmedium = pAttach->i_getMedium();
4715
4716 IMedium *iM = aMedium;
4717 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4718 if (aMedium && pMedium.isNull())
4719 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4720
4721 AutoCaller mediumCaller(pMedium);
4722 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4723
4724 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4725 if (pMedium)
4726 {
4727 DeviceType_T mediumType = pAttach->i_getType();
4728 switch (mediumType)
4729 {
4730 case DeviceType_DVD:
4731 case DeviceType_Floppy:
4732 break;
4733
4734 default:
4735 return setError(VBOX_E_INVALID_OBJECT_STATE,
4736 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4737 aControllerPort,
4738 aDevice,
4739 aName.c_str());
4740 }
4741 }
4742
4743 i_setModified(IsModified_Storage);
4744 mMediaData.backup();
4745
4746 {
4747 // The backup operation makes the pAttach reference point to the
4748 // old settings. Re-get the correct reference.
4749 pAttach = i_findAttachment(mMediaData->mAttachments,
4750 Bstr(aName).raw(),
4751 aControllerPort,
4752 aDevice);
4753 if (!oldmedium.isNull())
4754 oldmedium->i_removeBackReference(mData->mUuid);
4755 if (!pMedium.isNull())
4756 {
4757 pMedium->i_addBackReference(mData->mUuid);
4758
4759 mediumLock.release();
4760 multiLock.release();
4761 i_addMediumToRegistry(pMedium);
4762 multiLock.acquire();
4763 mediumLock.acquire();
4764 }
4765
4766 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4767 pAttach->i_updateMedium(pMedium);
4768 }
4769
4770 i_setModified(IsModified_Storage);
4771
4772 mediumLock.release();
4773 multiLock.release();
4774 rc = i_onMediumChange(pAttach, aForce);
4775 multiLock.acquire();
4776 mediumLock.acquire();
4777
4778 /* On error roll back this change only. */
4779 if (FAILED(rc))
4780 {
4781 if (!pMedium.isNull())
4782 pMedium->i_removeBackReference(mData->mUuid);
4783 pAttach = i_findAttachment(mMediaData->mAttachments,
4784 Bstr(aName).raw(),
4785 aControllerPort,
4786 aDevice);
4787 /* If the attachment is gone in the meantime, bail out. */
4788 if (pAttach.isNull())
4789 return rc;
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791 if (!oldmedium.isNull())
4792 oldmedium->i_addBackReference(mData->mUuid);
4793 pAttach->i_updateMedium(oldmedium);
4794 }
4795
4796 mediumLock.release();
4797 multiLock.release();
4798
4799 /* Save modified registries, but skip this machine as it's the caller's
4800 * job to save its settings like all other settings changes. */
4801 mParent->i_unmarkRegistryModified(i_getId());
4802 mParent->i_saveModifiedRegistries();
4803
4804 return rc;
4805}
4806HRESULT Machine::getMedium(const com::Utf8Str &aName,
4807 LONG aControllerPort,
4808 LONG aDevice,
4809 ComPtr<IMedium> &aMedium)
4810{
4811 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4812 aName.c_str(), aControllerPort, aDevice));
4813
4814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4815
4816 aMedium = NULL;
4817
4818 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4819 Bstr(aName).raw(),
4820 aControllerPort,
4821 aDevice);
4822 if (pAttach.isNull())
4823 return setError(VBOX_E_OBJECT_NOT_FOUND,
4824 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4825 aDevice, aControllerPort, aName.c_str());
4826
4827 aMedium = pAttach->i_getMedium();
4828
4829 return S_OK;
4830}
4831
4832HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4833{
4834
4835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4836
4837 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4838
4839 return S_OK;
4840}
4841
4842HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4843{
4844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4845
4846 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4847
4848 return S_OK;
4849}
4850
4851HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4852{
4853 /* Do not assert if slot is out of range, just return the advertised
4854 status. testdriver/vbox.py triggers this in logVmInfo. */
4855 if (aSlot >= mNetworkAdapters.size())
4856 return setError(E_INVALIDARG,
4857 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4858 aSlot, mNetworkAdapters.size());
4859
4860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4861
4862 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4863
4864 return S_OK;
4865}
4866
4867HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4868{
4869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4870
4871 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4872 size_t i = 0;
4873 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4874 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4875 ++it, ++i)
4876 aKeys[i] = it->first;
4877
4878 return S_OK;
4879}
4880
4881 /**
4882 * @note Locks this object for reading.
4883 */
4884HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4885 com::Utf8Str &aValue)
4886{
4887 /* start with nothing found */
4888 aValue = "";
4889
4890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4891
4892 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4893 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4894 // found:
4895 aValue = it->second; // source is a Utf8Str
4896
4897 /* return the result to caller (may be empty) */
4898 return S_OK;
4899}
4900
4901 /**
4902 * @note Locks mParent for writing + this object for writing.
4903 */
4904HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4905{
4906 Utf8Str strOldValue; // empty
4907
4908 // locking note: we only hold the read lock briefly to look up the old value,
4909 // then release it and call the onExtraCanChange callbacks. There is a small
4910 // chance of a race insofar as the callback might be called twice if two callers
4911 // change the same key at the same time, but that's a much better solution
4912 // than the deadlock we had here before. The actual changing of the extradata
4913 // is then performed under the write lock and race-free.
4914
4915 // look up the old value first; if nothing has changed then we need not do anything
4916 {
4917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4918
4919 // For snapshots don't even think about allowing changes, extradata
4920 // is global for a machine, so there is nothing snapshot specific.
4921 if (i_isSnapshotMachine())
4922 return setError(VBOX_E_INVALID_VM_STATE,
4923 tr("Cannot set extradata for a snapshot"));
4924
4925 // check if the right IMachine instance is used
4926 if (mData->mRegistered && !i_isSessionMachine())
4927 return setError(VBOX_E_INVALID_VM_STATE,
4928 tr("Cannot set extradata for an immutable machine"));
4929
4930 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4931 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4932 strOldValue = it->second;
4933 }
4934
4935 bool fChanged;
4936 if ((fChanged = (strOldValue != aValue)))
4937 {
4938 // ask for permission from all listeners outside the locks;
4939 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4940 // lock to copy the list of callbacks to invoke
4941 Bstr error;
4942 Bstr bstrValue(aValue);
4943
4944 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4945 {
4946 const char *sep = error.isEmpty() ? "" : ": ";
4947 CBSTR err = error.raw();
4948 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4949 return setError(E_ACCESSDENIED,
4950 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4951 aKey.c_str(),
4952 aValue.c_str(),
4953 sep,
4954 err);
4955 }
4956
4957 // data is changing and change not vetoed: then write it out under the lock
4958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4959
4960 if (aValue.isEmpty())
4961 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4962 else
4963 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4964 // creates a new key if needed
4965
4966 bool fNeedsGlobalSaveSettings = false;
4967 // This saving of settings is tricky: there is no "old state" for the
4968 // extradata items at all (unlike all other settings), so the old/new
4969 // settings comparison would give a wrong result!
4970 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4971
4972 if (fNeedsGlobalSaveSettings)
4973 {
4974 // save the global settings; for that we should hold only the VirtualBox lock
4975 alock.release();
4976 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4977 mParent->i_saveSettings();
4978 }
4979 }
4980
4981 // fire notification outside the lock
4982 if (fChanged)
4983 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4984
4985 return S_OK;
4986}
4987
4988HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4989{
4990 aProgress = NULL;
4991 NOREF(aSettingsFilePath);
4992 ReturnComNotImplemented();
4993}
4994
4995HRESULT Machine::saveSettings()
4996{
4997 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4998
4999 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5000 if (FAILED(rc)) return rc;
5001
5002 /* the settings file path may never be null */
5003 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5004
5005 /* save all VM data excluding snapshots */
5006 bool fNeedsGlobalSaveSettings = false;
5007 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5008 mlock.release();
5009
5010 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5011 {
5012 // save the global settings; for that we should hold only the VirtualBox lock
5013 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5014 rc = mParent->i_saveSettings();
5015 }
5016
5017 return rc;
5018}
5019
5020
5021HRESULT Machine::discardSettings()
5022{
5023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5024
5025 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5026 if (FAILED(rc)) return rc;
5027
5028 /*
5029 * during this rollback, the session will be notified if data has
5030 * been actually changed
5031 */
5032 i_rollback(true /* aNotify */);
5033
5034 return S_OK;
5035}
5036
5037/** @note Locks objects! */
5038HRESULT Machine::unregister(AutoCaller &autoCaller,
5039 CleanupMode_T aCleanupMode,
5040 std::vector<ComPtr<IMedium> > &aMedia)
5041{
5042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5043
5044 Guid id(i_getId());
5045
5046 if (mData->mSession.mState != SessionState_Unlocked)
5047 return setError(VBOX_E_INVALID_OBJECT_STATE,
5048 tr("Cannot unregister the machine '%s' while it is locked"),
5049 mUserData->s.strName.c_str());
5050
5051 // wait for state dependents to drop to zero
5052 i_ensureNoStateDependencies();
5053
5054 if (!mData->mAccessible)
5055 {
5056 // inaccessible maschines can only be unregistered; uninitialize ourselves
5057 // here because currently there may be no unregistered that are inaccessible
5058 // (this state combination is not supported). Note releasing the caller and
5059 // leaving the lock before calling uninit()
5060 alock.release();
5061 autoCaller.release();
5062
5063 uninit();
5064
5065 mParent->i_unregisterMachine(this, id);
5066 // calls VirtualBox::i_saveSettings()
5067
5068 return S_OK;
5069 }
5070
5071 HRESULT rc = S_OK;
5072
5073 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5074 // discard saved state
5075 if (mData->mMachineState == MachineState_Saved)
5076 {
5077 // add the saved state file to the list of files the caller should delete
5078 Assert(!mSSData->strStateFilePath.isEmpty());
5079 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5080
5081 mSSData->strStateFilePath.setNull();
5082
5083 // unconditionally set the machine state to powered off, we now
5084 // know no session has locked the machine
5085 mData->mMachineState = MachineState_PoweredOff;
5086 }
5087
5088 size_t cSnapshots = 0;
5089 if (mData->mFirstSnapshot)
5090 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5091 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5092 // fail now before we start detaching media
5093 return setError(VBOX_E_INVALID_OBJECT_STATE,
5094 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5095 mUserData->s.strName.c_str(), cSnapshots);
5096
5097 // This list collects the medium objects from all medium attachments
5098 // which we will detach from the machine and its snapshots, in a specific
5099 // order which allows for closing all media without getting "media in use"
5100 // errors, simply by going through the list from the front to the back:
5101 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5102 // and must be closed before the parent media from the snapshots, or closing the parents
5103 // will fail because they still have children);
5104 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5105 // the root ("first") snapshot of the machine.
5106 MediaList llMedia;
5107
5108 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5109 && mMediaData->mAttachments.size()
5110 )
5111 {
5112 // we have media attachments: detach them all and add the Medium objects to our list
5113 if (aCleanupMode != CleanupMode_UnregisterOnly)
5114 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5115 else
5116 return setError(VBOX_E_INVALID_OBJECT_STATE,
5117 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5118 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5119 }
5120
5121 if (cSnapshots)
5122 {
5123 // add the media from the medium attachments of the snapshots to llMedia
5124 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5125 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5126 // into the children first
5127
5128 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5129 MachineState_T oldState = mData->mMachineState;
5130 mData->mMachineState = MachineState_DeletingSnapshot;
5131
5132 // make a copy of the first snapshot so the refcount does not drop to 0
5133 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5134 // because of the AutoCaller voodoo)
5135 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5136
5137 // GO!
5138 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5139
5140 mData->mMachineState = oldState;
5141 }
5142
5143 if (FAILED(rc))
5144 {
5145 i_rollbackMedia();
5146 return rc;
5147 }
5148
5149 // commit all the media changes made above
5150 i_commitMedia();
5151
5152 mData->mRegistered = false;
5153
5154 // machine lock no longer needed
5155 alock.release();
5156
5157 // return media to caller
5158 size_t i = 0;
5159 aMedia.resize(llMedia.size());
5160 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5161 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5162
5163 mParent->i_unregisterMachine(this, id);
5164 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5165
5166 return S_OK;
5167}
5168
5169/**
5170 * Task record for deleting a machine config.
5171 */
5172struct Machine::DeleteConfigTask
5173 : public Machine::Task
5174{
5175 DeleteConfigTask(Machine *m,
5176 Progress *p,
5177 const Utf8Str &t,
5178 const RTCList<ComPtr<IMedium> > &llMediums,
5179 const StringsList &llFilesToDelete)
5180 : Task(m, p, t),
5181 m_llMediums(llMediums),
5182 m_llFilesToDelete(llFilesToDelete)
5183 {}
5184
5185 void handler()
5186 {
5187 m_pMachine->i_deleteConfigHandler(*this);
5188 }
5189
5190 RTCList<ComPtr<IMedium> > m_llMediums;
5191 StringsList m_llFilesToDelete;
5192};
5193
5194/**
5195 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5196 * SessionMachine::taskHandler().
5197 *
5198 * @note Locks this object for writing.
5199 *
5200 * @param task
5201 * @return
5202 */
5203void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5204{
5205 LogFlowThisFuncEnter();
5206
5207 AutoCaller autoCaller(this);
5208 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5209 if (FAILED(autoCaller.rc()))
5210 {
5211 /* we might have been uninitialized because the session was accidentally
5212 * closed by the client, so don't assert */
5213 HRESULT rc = setError(E_FAIL,
5214 tr("The session has been accidentally closed"));
5215 task.m_pProgress->i_notifyComplete(rc);
5216 LogFlowThisFuncLeave();
5217 return;
5218 }
5219
5220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5221
5222 HRESULT rc = S_OK;
5223
5224 try
5225 {
5226 ULONG uLogHistoryCount = 3;
5227 ComPtr<ISystemProperties> systemProperties;
5228 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5229 if (FAILED(rc)) throw rc;
5230
5231 if (!systemProperties.isNull())
5232 {
5233 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5234 if (FAILED(rc)) throw rc;
5235 }
5236
5237 MachineState_T oldState = mData->mMachineState;
5238 i_setMachineState(MachineState_SettingUp);
5239 alock.release();
5240 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5241 {
5242 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5243 {
5244 AutoCaller mac(pMedium);
5245 if (FAILED(mac.rc())) throw mac.rc();
5246 Utf8Str strLocation = pMedium->i_getLocationFull();
5247 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5248 if (FAILED(rc)) throw rc;
5249 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5250 }
5251 if (pMedium->i_isMediumFormatFile())
5252 {
5253 ComPtr<IProgress> pProgress2;
5254 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5255 if (FAILED(rc)) throw rc;
5256 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5257 if (FAILED(rc)) throw rc;
5258 /* Check the result of the asynchronous process. */
5259 LONG iRc;
5260 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5261 if (FAILED(rc)) throw rc;
5262 /* If the thread of the progress object has an error, then
5263 * retrieve the error info from there, or it'll be lost. */
5264 if (FAILED(iRc))
5265 throw setError(ProgressErrorInfo(pProgress2));
5266 }
5267
5268 /* Close the medium, deliberately without checking the return
5269 * code, and without leaving any trace in the error info, as
5270 * a failure here is a very minor issue, which shouldn't happen
5271 * as above we even managed to delete the medium. */
5272 {
5273 ErrorInfoKeeper eik;
5274 pMedium->Close();
5275 }
5276 }
5277 i_setMachineState(oldState);
5278 alock.acquire();
5279
5280 // delete the files pushed on the task list by Machine::Delete()
5281 // (this includes saved states of the machine and snapshots and
5282 // medium storage files from the IMedium list passed in, and the
5283 // machine XML file)
5284 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5285 while (it != task.m_llFilesToDelete.end())
5286 {
5287 const Utf8Str &strFile = *it;
5288 LogFunc(("Deleting file %s\n", strFile.c_str()));
5289 int vrc = RTFileDelete(strFile.c_str());
5290 if (RT_FAILURE(vrc))
5291 throw setError(VBOX_E_IPRT_ERROR,
5292 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5293
5294 ++it;
5295 if (it == task.m_llFilesToDelete.end())
5296 {
5297 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5298 if (FAILED(rc)) throw rc;
5299 break;
5300 }
5301
5302 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5303 if (FAILED(rc)) throw rc;
5304 }
5305
5306 /* delete the settings only when the file actually exists */
5307 if (mData->pMachineConfigFile->fileExists())
5308 {
5309 /* Delete any backup or uncommitted XML files. Ignore failures.
5310 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5311 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5312 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5313 RTFileDelete(otherXml.c_str());
5314 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5315 RTFileDelete(otherXml.c_str());
5316
5317 /* delete the Logs folder, nothing important should be left
5318 * there (we don't check for errors because the user might have
5319 * some private files there that we don't want to delete) */
5320 Utf8Str logFolder;
5321 getLogFolder(logFolder);
5322 Assert(logFolder.length());
5323 if (RTDirExists(logFolder.c_str()))
5324 {
5325 /* Delete all VBox.log[.N] files from the Logs folder
5326 * (this must be in sync with the rotation logic in
5327 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5328 * files that may have been created by the GUI. */
5329 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5330 logFolder.c_str(), RTPATH_DELIMITER);
5331 RTFileDelete(log.c_str());
5332 log = Utf8StrFmt("%s%cVBox.png",
5333 logFolder.c_str(), RTPATH_DELIMITER);
5334 RTFileDelete(log.c_str());
5335 for (int i = uLogHistoryCount; i > 0; i--)
5336 {
5337 log = Utf8StrFmt("%s%cVBox.log.%d",
5338 logFolder.c_str(), RTPATH_DELIMITER, i);
5339 RTFileDelete(log.c_str());
5340 log = Utf8StrFmt("%s%cVBox.png.%d",
5341 logFolder.c_str(), RTPATH_DELIMITER, i);
5342 RTFileDelete(log.c_str());
5343 }
5344#if defined(RT_OS_WINDOWS)
5345 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5346 RTFileDelete(log.c_str());
5347 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5348 RTFileDelete(log.c_str());
5349#endif
5350
5351 RTDirRemove(logFolder.c_str());
5352 }
5353
5354 /* delete the Snapshots folder, nothing important should be left
5355 * there (we don't check for errors because the user might have
5356 * some private files there that we don't want to delete) */
5357 Utf8Str strFullSnapshotFolder;
5358 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5359 Assert(!strFullSnapshotFolder.isEmpty());
5360 if (RTDirExists(strFullSnapshotFolder.c_str()))
5361 RTDirRemove(strFullSnapshotFolder.c_str());
5362
5363 // delete the directory that contains the settings file, but only
5364 // if it matches the VM name
5365 Utf8Str settingsDir;
5366 if (i_isInOwnDir(&settingsDir))
5367 RTDirRemove(settingsDir.c_str());
5368 }
5369
5370 alock.release();
5371
5372 mParent->i_saveModifiedRegistries();
5373 }
5374 catch (HRESULT aRC) { rc = aRC; }
5375
5376 task.m_pProgress->i_notifyComplete(rc);
5377
5378 LogFlowThisFuncLeave();
5379}
5380
5381HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5382{
5383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5384
5385 HRESULT rc = i_checkStateDependency(MutableStateDep);
5386 if (FAILED(rc)) return rc;
5387
5388 if (mData->mRegistered)
5389 return setError(VBOX_E_INVALID_VM_STATE,
5390 tr("Cannot delete settings of a registered machine"));
5391
5392 // collect files to delete
5393 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5394 if (mData->pMachineConfigFile->fileExists())
5395 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5396
5397 RTCList<ComPtr<IMedium> > llMediums;
5398 for (size_t i = 0; i < aMedia.size(); ++i)
5399 {
5400 IMedium *pIMedium(aMedia[i]);
5401 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5402 if (pMedium.isNull())
5403 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5404 SafeArray<BSTR> ids;
5405 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5406 if (FAILED(rc)) return rc;
5407 /* At this point the medium should not have any back references
5408 * anymore. If it has it is attached to another VM and *must* not
5409 * deleted. */
5410 if (ids.size() < 1)
5411 llMediums.append(pMedium);
5412 }
5413
5414 ComObjPtr<Progress> pProgress;
5415 pProgress.createObject();
5416 rc = pProgress->init(i_getVirtualBox(),
5417 static_cast<IMachine*>(this) /* aInitiator */,
5418 Bstr(tr("Deleting files")).raw(),
5419 true /* fCancellable */,
5420 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5421 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5422 if (FAILED(rc))
5423 return rc;
5424
5425 /* create and start the task on a separate thread (note that it will not
5426 * start working until we release alock) */
5427 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5428 rc = pTask->createThread();
5429 if (FAILED(rc))
5430 return rc;
5431
5432 pProgress.queryInterfaceTo(aProgress.asOutParam());
5433
5434 LogFlowFuncLeave();
5435
5436 return S_OK;
5437}
5438
5439HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5440{
5441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5442
5443 ComObjPtr<Snapshot> pSnapshot;
5444 HRESULT rc;
5445
5446 if (aNameOrId.isEmpty())
5447 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5448 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5449 else
5450 {
5451 Guid uuid(aNameOrId);
5452 if (uuid.isValid())
5453 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5454 else
5455 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5456 }
5457 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5458
5459 return rc;
5460}
5461
5462HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5463{
5464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5465
5466 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5467 if (FAILED(rc)) return rc;
5468
5469 ComObjPtr<SharedFolder> sharedFolder;
5470 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5471 if (SUCCEEDED(rc))
5472 return setError(VBOX_E_OBJECT_IN_USE,
5473 tr("Shared folder named '%s' already exists"),
5474 aName.c_str());
5475
5476 sharedFolder.createObject();
5477 rc = sharedFolder->init(i_getMachine(),
5478 aName,
5479 aHostPath,
5480 !!aWritable,
5481 !!aAutomount,
5482 true /* fFailOnError */);
5483 if (FAILED(rc)) return rc;
5484
5485 i_setModified(IsModified_SharedFolders);
5486 mHWData.backup();
5487 mHWData->mSharedFolders.push_back(sharedFolder);
5488
5489 /* inform the direct session if any */
5490 alock.release();
5491 i_onSharedFolderChange();
5492
5493 return S_OK;
5494}
5495
5496HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5497{
5498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5499
5500 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5501 if (FAILED(rc)) return rc;
5502
5503 ComObjPtr<SharedFolder> sharedFolder;
5504 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5505 if (FAILED(rc)) return rc;
5506
5507 i_setModified(IsModified_SharedFolders);
5508 mHWData.backup();
5509 mHWData->mSharedFolders.remove(sharedFolder);
5510
5511 /* inform the direct session if any */
5512 alock.release();
5513 i_onSharedFolderChange();
5514
5515 return S_OK;
5516}
5517
5518HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5519{
5520 /* start with No */
5521 *aCanShow = FALSE;
5522
5523 ComPtr<IInternalSessionControl> directControl;
5524 {
5525 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5526
5527 if (mData->mSession.mState != SessionState_Locked)
5528 return setError(VBOX_E_INVALID_VM_STATE,
5529 tr("Machine is not locked for session (session state: %s)"),
5530 Global::stringifySessionState(mData->mSession.mState));
5531
5532 if (mData->mSession.mLockType == LockType_VM)
5533 directControl = mData->mSession.mDirectControl;
5534 }
5535
5536 /* ignore calls made after #OnSessionEnd() is called */
5537 if (!directControl)
5538 return S_OK;
5539
5540 LONG64 dummy;
5541 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5542}
5543
5544HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5545{
5546 ComPtr<IInternalSessionControl> directControl;
5547 {
5548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5549
5550 if (mData->mSession.mState != SessionState_Locked)
5551 return setError(E_FAIL,
5552 tr("Machine is not locked for session (session state: %s)"),
5553 Global::stringifySessionState(mData->mSession.mState));
5554
5555 if (mData->mSession.mLockType == LockType_VM)
5556 directControl = mData->mSession.mDirectControl;
5557 }
5558
5559 /* ignore calls made after #OnSessionEnd() is called */
5560 if (!directControl)
5561 return S_OK;
5562
5563 BOOL dummy;
5564 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5565}
5566
5567#ifdef VBOX_WITH_GUEST_PROPS
5568/**
5569 * Look up a guest property in VBoxSVC's internal structures.
5570 */
5571HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5572 com::Utf8Str &aValue,
5573 LONG64 *aTimestamp,
5574 com::Utf8Str &aFlags) const
5575{
5576 using namespace guestProp;
5577
5578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5579 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5580
5581 if (it != mHWData->mGuestProperties.end())
5582 {
5583 char szFlags[MAX_FLAGS_LEN + 1];
5584 aValue = it->second.strValue;
5585 *aTimestamp = it->second.mTimestamp;
5586 writeFlags(it->second.mFlags, szFlags);
5587 aFlags = Utf8Str(szFlags);
5588 }
5589
5590 return S_OK;
5591}
5592
5593/**
5594 * Query the VM that a guest property belongs to for the property.
5595 * @returns E_ACCESSDENIED if the VM process is not available or not
5596 * currently handling queries and the lookup should then be done in
5597 * VBoxSVC.
5598 */
5599HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5600 com::Utf8Str &aValue,
5601 LONG64 *aTimestamp,
5602 com::Utf8Str &aFlags) const
5603{
5604 HRESULT rc = S_OK;
5605 BSTR bValue = NULL;
5606 BSTR bFlags = NULL;
5607
5608 ComPtr<IInternalSessionControl> directControl;
5609 {
5610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5611 if (mData->mSession.mLockType == LockType_VM)
5612 directControl = mData->mSession.mDirectControl;
5613 }
5614
5615 /* ignore calls made after #OnSessionEnd() is called */
5616 if (!directControl)
5617 rc = E_ACCESSDENIED;
5618 else
5619 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5620 0 /* accessMode */,
5621 &bValue, aTimestamp, &bFlags);
5622
5623 aValue = bValue;
5624 aFlags = bFlags;
5625
5626 return rc;
5627}
5628#endif // VBOX_WITH_GUEST_PROPS
5629
5630HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5631 com::Utf8Str &aValue,
5632 LONG64 *aTimestamp,
5633 com::Utf8Str &aFlags)
5634{
5635#ifndef VBOX_WITH_GUEST_PROPS
5636 ReturnComNotImplemented();
5637#else // VBOX_WITH_GUEST_PROPS
5638
5639 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5640
5641 if (rc == E_ACCESSDENIED)
5642 /* The VM is not running or the service is not (yet) accessible */
5643 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5644 return rc;
5645#endif // VBOX_WITH_GUEST_PROPS
5646}
5647
5648HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5649{
5650 LONG64 dummyTimestamp;
5651 com::Utf8Str dummyFlags;
5652 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5653 return rc;
5654
5655}
5656HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5657{
5658 com::Utf8Str dummyFlags;
5659 com::Utf8Str dummyValue;
5660 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5661 return rc;
5662}
5663
5664#ifdef VBOX_WITH_GUEST_PROPS
5665/**
5666 * Set a guest property in VBoxSVC's internal structures.
5667 */
5668HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5669 const com::Utf8Str &aFlags, bool fDelete)
5670{
5671 using namespace guestProp;
5672
5673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5674 HRESULT rc = S_OK;
5675
5676 rc = i_checkStateDependency(MutableOrSavedStateDep);
5677 if (FAILED(rc)) return rc;
5678
5679 try
5680 {
5681 uint32_t fFlags = NILFLAG;
5682 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5683 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5684
5685 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5686 if (it == mHWData->mGuestProperties.end())
5687 {
5688 if (!fDelete)
5689 {
5690 i_setModified(IsModified_MachineData);
5691 mHWData.backupEx();
5692
5693 RTTIMESPEC time;
5694 HWData::GuestProperty prop;
5695 prop.strValue = Bstr(aValue).raw();
5696 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5697 prop.mFlags = fFlags;
5698 mHWData->mGuestProperties[aName] = prop;
5699 }
5700 }
5701 else
5702 {
5703 if (it->second.mFlags & (RDONLYHOST))
5704 {
5705 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5706 }
5707 else
5708 {
5709 i_setModified(IsModified_MachineData);
5710 mHWData.backupEx();
5711
5712 /* The backupEx() operation invalidates our iterator,
5713 * so get a new one. */
5714 it = mHWData->mGuestProperties.find(aName);
5715 Assert(it != mHWData->mGuestProperties.end());
5716
5717 if (!fDelete)
5718 {
5719 RTTIMESPEC time;
5720 it->second.strValue = aValue;
5721 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5722 it->second.mFlags = fFlags;
5723 }
5724 else
5725 mHWData->mGuestProperties.erase(it);
5726 }
5727 }
5728
5729 if (SUCCEEDED(rc))
5730 {
5731 alock.release();
5732
5733 mParent->i_onGuestPropertyChange(mData->mUuid,
5734 Bstr(aName).raw(),
5735 Bstr(aValue).raw(),
5736 Bstr(aFlags).raw());
5737 }
5738 }
5739 catch (std::bad_alloc &)
5740 {
5741 rc = E_OUTOFMEMORY;
5742 }
5743
5744 return rc;
5745}
5746
5747/**
5748 * Set a property on the VM that that property belongs to.
5749 * @returns E_ACCESSDENIED if the VM process is not available or not
5750 * currently handling queries and the setting should then be done in
5751 * VBoxSVC.
5752 */
5753HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5754 const com::Utf8Str &aFlags, bool fDelete)
5755{
5756 HRESULT rc;
5757
5758 try
5759 {
5760 ComPtr<IInternalSessionControl> directControl;
5761 {
5762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5763 if (mData->mSession.mLockType == LockType_VM)
5764 directControl = mData->mSession.mDirectControl;
5765 }
5766
5767 BSTR dummy = NULL; /* will not be changed (setter) */
5768 LONG64 dummy64;
5769 if (!directControl)
5770 rc = E_ACCESSDENIED;
5771 else
5772 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5773 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5774 fDelete? 2: 1 /* accessMode */,
5775 &dummy, &dummy64, &dummy);
5776 }
5777 catch (std::bad_alloc &)
5778 {
5779 rc = E_OUTOFMEMORY;
5780 }
5781
5782 return rc;
5783}
5784#endif // VBOX_WITH_GUEST_PROPS
5785
5786HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5787 const com::Utf8Str &aFlags)
5788{
5789#ifndef VBOX_WITH_GUEST_PROPS
5790 ReturnComNotImplemented();
5791#else // VBOX_WITH_GUEST_PROPS
5792 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5793 if (rc == E_ACCESSDENIED)
5794 /* The VM is not running or the service is not (yet) accessible */
5795 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5796 return rc;
5797#endif // VBOX_WITH_GUEST_PROPS
5798}
5799
5800HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5801{
5802 return setGuestProperty(aProperty, aValue, "");
5803}
5804
5805HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5806{
5807#ifndef VBOX_WITH_GUEST_PROPS
5808 ReturnComNotImplemented();
5809#else // VBOX_WITH_GUEST_PROPS
5810 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5811 if (rc == E_ACCESSDENIED)
5812 /* The VM is not running or the service is not (yet) accessible */
5813 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5814 return rc;
5815#endif // VBOX_WITH_GUEST_PROPS
5816}
5817
5818#ifdef VBOX_WITH_GUEST_PROPS
5819/**
5820 * Enumerate the guest properties in VBoxSVC's internal structures.
5821 */
5822HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5823 std::vector<com::Utf8Str> &aNames,
5824 std::vector<com::Utf8Str> &aValues,
5825 std::vector<LONG64> &aTimestamps,
5826 std::vector<com::Utf8Str> &aFlags)
5827{
5828 using namespace guestProp;
5829
5830 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5831 Utf8Str strPatterns(aPatterns);
5832
5833 HWData::GuestPropertyMap propMap;
5834
5835 /*
5836 * Look for matching patterns and build up a list.
5837 */
5838 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5839 while (it != mHWData->mGuestProperties.end())
5840 {
5841 if ( strPatterns.isEmpty()
5842 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5843 RTSTR_MAX,
5844 it->first.c_str(),
5845 RTSTR_MAX,
5846 NULL)
5847 )
5848 propMap.insert(*it);
5849 ++it;
5850 }
5851
5852 alock.release();
5853
5854 /*
5855 * And build up the arrays for returning the property information.
5856 */
5857 size_t cEntries = propMap.size();
5858
5859 aNames.resize(cEntries);
5860 aValues.resize(cEntries);
5861 aTimestamps.resize(cEntries);
5862 aFlags.resize(cEntries);
5863
5864 char szFlags[MAX_FLAGS_LEN + 1];
5865 size_t i= 0;
5866 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5867 {
5868 aNames[i] = it->first;
5869 aValues[i] = it->second.strValue;
5870 aTimestamps[i] = it->second.mTimestamp;
5871 writeFlags(it->second.mFlags, szFlags);
5872 aFlags[i] = Utf8Str(szFlags);
5873 }
5874
5875 return S_OK;
5876}
5877
5878/**
5879 * Enumerate the properties managed by a VM.
5880 * @returns E_ACCESSDENIED if the VM process is not available or not
5881 * currently handling queries and the setting should then be done in
5882 * VBoxSVC.
5883 */
5884HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5885 std::vector<com::Utf8Str> &aNames,
5886 std::vector<com::Utf8Str> &aValues,
5887 std::vector<LONG64> &aTimestamps,
5888 std::vector<com::Utf8Str> &aFlags)
5889{
5890 HRESULT rc;
5891 ComPtr<IInternalSessionControl> directControl;
5892 {
5893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5894 if (mData->mSession.mLockType == LockType_VM)
5895 directControl = mData->mSession.mDirectControl;
5896 }
5897
5898 com::SafeArray<BSTR> bNames;
5899 com::SafeArray<BSTR> bValues;
5900 com::SafeArray<LONG64> bTimestamps;
5901 com::SafeArray<BSTR> bFlags;
5902
5903 if (!directControl)
5904 rc = E_ACCESSDENIED;
5905 else
5906 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5907 ComSafeArrayAsOutParam(bNames),
5908 ComSafeArrayAsOutParam(bValues),
5909 ComSafeArrayAsOutParam(bTimestamps),
5910 ComSafeArrayAsOutParam(bFlags));
5911 size_t i;
5912 aNames.resize(bNames.size());
5913 for (i = 0; i < bNames.size(); ++i)
5914 aNames[i] = Utf8Str(bNames[i]);
5915 aValues.resize(bValues.size());
5916 for (i = 0; i < bValues.size(); ++i)
5917 aValues[i] = Utf8Str(bValues[i]);
5918 aTimestamps.resize(bTimestamps.size());
5919 for (i = 0; i < bTimestamps.size(); ++i)
5920 aTimestamps[i] = bTimestamps[i];
5921 aFlags.resize(bFlags.size());
5922 for (i = 0; i < bFlags.size(); ++i)
5923 aFlags[i] = Utf8Str(bFlags[i]);
5924
5925 return rc;
5926}
5927#endif // VBOX_WITH_GUEST_PROPS
5928HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5929 std::vector<com::Utf8Str> &aNames,
5930 std::vector<com::Utf8Str> &aValues,
5931 std::vector<LONG64> &aTimestamps,
5932 std::vector<com::Utf8Str> &aFlags)
5933{
5934#ifndef VBOX_WITH_GUEST_PROPS
5935 ReturnComNotImplemented();
5936#else // VBOX_WITH_GUEST_PROPS
5937
5938 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5939
5940 if (rc == E_ACCESSDENIED)
5941 /* The VM is not running or the service is not (yet) accessible */
5942 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5943 return rc;
5944#endif // VBOX_WITH_GUEST_PROPS
5945}
5946
5947HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5948 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5949{
5950 MediaData::AttachmentList atts;
5951
5952 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5953 if (FAILED(rc)) return rc;
5954
5955 size_t i = 0;
5956 aMediumAttachments.resize(atts.size());
5957 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5958 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5959
5960 return S_OK;
5961}
5962
5963HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5964 LONG aControllerPort,
5965 LONG aDevice,
5966 ComPtr<IMediumAttachment> &aAttachment)
5967{
5968 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5969 aName.c_str(), aControllerPort, aDevice));
5970
5971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5972
5973 aAttachment = NULL;
5974
5975 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5976 Bstr(aName).raw(),
5977 aControllerPort,
5978 aDevice);
5979 if (pAttach.isNull())
5980 return setError(VBOX_E_OBJECT_NOT_FOUND,
5981 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5982 aDevice, aControllerPort, aName.c_str());
5983
5984 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5985
5986 return S_OK;
5987}
5988
5989
5990HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5991 StorageBus_T aConnectionType,
5992 ComPtr<IStorageController> &aController)
5993{
5994 if ( (aConnectionType <= StorageBus_Null)
5995 || (aConnectionType > StorageBus_USB))
5996 return setError(E_INVALIDARG,
5997 tr("Invalid connection type: %d"),
5998 aConnectionType);
5999
6000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6001
6002 HRESULT rc = i_checkStateDependency(MutableStateDep);
6003 if (FAILED(rc)) return rc;
6004
6005 /* try to find one with the name first. */
6006 ComObjPtr<StorageController> ctrl;
6007
6008 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6009 if (SUCCEEDED(rc))
6010 return setError(VBOX_E_OBJECT_IN_USE,
6011 tr("Storage controller named '%s' already exists"),
6012 aName.c_str());
6013
6014 ctrl.createObject();
6015
6016 /* get a new instance number for the storage controller */
6017 ULONG ulInstance = 0;
6018 bool fBootable = true;
6019 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6020 it != mStorageControllers->end();
6021 ++it)
6022 {
6023 if ((*it)->i_getStorageBus() == aConnectionType)
6024 {
6025 ULONG ulCurInst = (*it)->i_getInstance();
6026
6027 if (ulCurInst >= ulInstance)
6028 ulInstance = ulCurInst + 1;
6029
6030 /* Only one controller of each type can be marked as bootable. */
6031 if ((*it)->i_getBootable())
6032 fBootable = false;
6033 }
6034 }
6035
6036 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6037 if (FAILED(rc)) return rc;
6038
6039 i_setModified(IsModified_Storage);
6040 mStorageControllers.backup();
6041 mStorageControllers->push_back(ctrl);
6042
6043 ctrl.queryInterfaceTo(aController.asOutParam());
6044
6045 /* inform the direct session if any */
6046 alock.release();
6047 i_onStorageControllerChange();
6048
6049 return S_OK;
6050}
6051
6052HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6053 ComPtr<IStorageController> &aStorageController)
6054{
6055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6056
6057 ComObjPtr<StorageController> ctrl;
6058
6059 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6060 if (SUCCEEDED(rc))
6061 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6062
6063 return rc;
6064}
6065
6066HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6067 ULONG aInstance,
6068 ComPtr<IStorageController> &aStorageController)
6069{
6070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6071
6072 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6073 it != mStorageControllers->end();
6074 ++it)
6075 {
6076 if ( (*it)->i_getStorageBus() == aConnectionType
6077 && (*it)->i_getInstance() == aInstance)
6078 {
6079 (*it).queryInterfaceTo(aStorageController.asOutParam());
6080 return S_OK;
6081 }
6082 }
6083
6084 return setError(VBOX_E_OBJECT_NOT_FOUND,
6085 tr("Could not find a storage controller with instance number '%lu'"),
6086 aInstance);
6087}
6088
6089HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6090{
6091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6092
6093 HRESULT rc = i_checkStateDependency(MutableStateDep);
6094 if (FAILED(rc)) return rc;
6095
6096 ComObjPtr<StorageController> ctrl;
6097
6098 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6099 if (SUCCEEDED(rc))
6100 {
6101 /* Ensure that only one controller of each type is marked as bootable. */
6102 if (aBootable == TRUE)
6103 {
6104 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6105 it != mStorageControllers->end();
6106 ++it)
6107 {
6108 ComObjPtr<StorageController> aCtrl = (*it);
6109
6110 if ( (aCtrl->i_getName() != aName)
6111 && aCtrl->i_getBootable() == TRUE
6112 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6113 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6114 {
6115 aCtrl->i_setBootable(FALSE);
6116 break;
6117 }
6118 }
6119 }
6120
6121 if (SUCCEEDED(rc))
6122 {
6123 ctrl->i_setBootable(aBootable);
6124 i_setModified(IsModified_Storage);
6125 }
6126 }
6127
6128 if (SUCCEEDED(rc))
6129 {
6130 /* inform the direct session if any */
6131 alock.release();
6132 i_onStorageControllerChange();
6133 }
6134
6135 return rc;
6136}
6137
6138HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6139{
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 ComObjPtr<StorageController> ctrl;
6146 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6147 if (FAILED(rc)) return rc;
6148
6149 {
6150 /* find all attached devices to the appropriate storage controller and detach them all */
6151 // make a temporary list because detachDevice invalidates iterators into
6152 // mMediaData->mAttachments
6153 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6154
6155 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6156 it != llAttachments2.end();
6157 ++it)
6158 {
6159 MediumAttachment *pAttachTemp = *it;
6160
6161 AutoCaller localAutoCaller(pAttachTemp);
6162 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6163
6164 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6165
6166 if (pAttachTemp->i_getControllerName() == aName)
6167 {
6168 rc = i_detachDevice(pAttachTemp, alock, NULL);
6169 if (FAILED(rc)) return rc;
6170 }
6171 }
6172 }
6173
6174 /* We can remove it now. */
6175 i_setModified(IsModified_Storage);
6176 mStorageControllers.backup();
6177
6178 ctrl->i_unshare();
6179
6180 mStorageControllers->remove(ctrl);
6181
6182 /* inform the direct session if any */
6183 alock.release();
6184 i_onStorageControllerChange();
6185
6186 return S_OK;
6187}
6188
6189HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6190 ComPtr<IUSBController> &aController)
6191{
6192 if ( (aType <= USBControllerType_Null)
6193 || (aType >= USBControllerType_Last))
6194 return setError(E_INVALIDARG,
6195 tr("Invalid USB controller type: %d"),
6196 aType);
6197
6198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6199
6200 HRESULT rc = i_checkStateDependency(MutableStateDep);
6201 if (FAILED(rc)) return rc;
6202
6203 /* try to find one with the same type first. */
6204 ComObjPtr<USBController> ctrl;
6205
6206 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6207 if (SUCCEEDED(rc))
6208 return setError(VBOX_E_OBJECT_IN_USE,
6209 tr("USB controller named '%s' already exists"),
6210 aName.c_str());
6211
6212 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6213 ULONG maxInstances;
6214 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6215 if (FAILED(rc))
6216 return rc;
6217
6218 ULONG cInstances = i_getUSBControllerCountByType(aType);
6219 if (cInstances >= maxInstances)
6220 return setError(E_INVALIDARG,
6221 tr("Too many USB controllers of this type"));
6222
6223 ctrl.createObject();
6224
6225 rc = ctrl->init(this, aName, aType);
6226 if (FAILED(rc)) return rc;
6227
6228 i_setModified(IsModified_USB);
6229 mUSBControllers.backup();
6230 mUSBControllers->push_back(ctrl);
6231
6232 ctrl.queryInterfaceTo(aController.asOutParam());
6233
6234 /* inform the direct session if any */
6235 alock.release();
6236 i_onUSBControllerChange();
6237
6238 return S_OK;
6239}
6240
6241HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6242{
6243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6244
6245 ComObjPtr<USBController> ctrl;
6246
6247 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6248 if (SUCCEEDED(rc))
6249 ctrl.queryInterfaceTo(aController.asOutParam());
6250
6251 return rc;
6252}
6253
6254HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6255 ULONG *aControllers)
6256{
6257 if ( (aType <= USBControllerType_Null)
6258 || (aType >= USBControllerType_Last))
6259 return setError(E_INVALIDARG,
6260 tr("Invalid USB controller type: %d"),
6261 aType);
6262
6263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6264
6265 ComObjPtr<USBController> ctrl;
6266
6267 *aControllers = i_getUSBControllerCountByType(aType);
6268
6269 return S_OK;
6270}
6271
6272HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6273{
6274
6275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6276
6277 HRESULT rc = i_checkStateDependency(MutableStateDep);
6278 if (FAILED(rc)) return rc;
6279
6280 ComObjPtr<USBController> ctrl;
6281 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6282 if (FAILED(rc)) return rc;
6283
6284 i_setModified(IsModified_USB);
6285 mUSBControllers.backup();
6286
6287 ctrl->i_unshare();
6288
6289 mUSBControllers->remove(ctrl);
6290
6291 /* inform the direct session if any */
6292 alock.release();
6293 i_onUSBControllerChange();
6294
6295 return S_OK;
6296}
6297
6298HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6299 ULONG *aOriginX,
6300 ULONG *aOriginY,
6301 ULONG *aWidth,
6302 ULONG *aHeight,
6303 BOOL *aEnabled)
6304{
6305 uint32_t u32OriginX= 0;
6306 uint32_t u32OriginY= 0;
6307 uint32_t u32Width = 0;
6308 uint32_t u32Height = 0;
6309 uint16_t u16Flags = 0;
6310
6311 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6312 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6313 if (RT_FAILURE(vrc))
6314 {
6315#ifdef RT_OS_WINDOWS
6316 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6317 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6318 * So just assign fEnable to TRUE again.
6319 * The right fix would be to change GUI API wrappers to make sure that parameters
6320 * are changed only if API succeeds.
6321 */
6322 *aEnabled = TRUE;
6323#endif
6324 return setError(VBOX_E_IPRT_ERROR,
6325 tr("Saved guest size is not available (%Rrc)"),
6326 vrc);
6327 }
6328
6329 *aOriginX = u32OriginX;
6330 *aOriginY = u32OriginY;
6331 *aWidth = u32Width;
6332 *aHeight = u32Height;
6333 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6334
6335 return S_OK;
6336}
6337
6338HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6339 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6340{
6341 if (aScreenId != 0)
6342 return E_NOTIMPL;
6343
6344 if ( aBitmapFormat != BitmapFormat_BGR0
6345 && aBitmapFormat != BitmapFormat_BGRA
6346 && aBitmapFormat != BitmapFormat_RGBA
6347 && aBitmapFormat != BitmapFormat_PNG)
6348 return setError(E_NOTIMPL,
6349 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6350
6351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6352
6353 uint8_t *pu8Data = NULL;
6354 uint32_t cbData = 0;
6355 uint32_t u32Width = 0;
6356 uint32_t u32Height = 0;
6357
6358 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6359
6360 if (RT_FAILURE(vrc))
6361 return setError(VBOX_E_IPRT_ERROR,
6362 tr("Saved thumbnail data is not available (%Rrc)"),
6363 vrc);
6364
6365 HRESULT hr = S_OK;
6366
6367 *aWidth = u32Width;
6368 *aHeight = u32Height;
6369
6370 if (cbData > 0)
6371 {
6372 /* Convert pixels to the format expected by the API caller. */
6373 if (aBitmapFormat == BitmapFormat_BGR0)
6374 {
6375 /* [0] B, [1] G, [2] R, [3] 0. */
6376 aData.resize(cbData);
6377 memcpy(&aData.front(), pu8Data, cbData);
6378 }
6379 else if (aBitmapFormat == BitmapFormat_BGRA)
6380 {
6381 /* [0] B, [1] G, [2] R, [3] A. */
6382 aData.resize(cbData);
6383 for (uint32_t i = 0; i < cbData; i += 4)
6384 {
6385 aData[i] = pu8Data[i];
6386 aData[i + 1] = pu8Data[i + 1];
6387 aData[i + 2] = pu8Data[i + 2];
6388 aData[i + 3] = 0xff;
6389 }
6390 }
6391 else if (aBitmapFormat == BitmapFormat_RGBA)
6392 {
6393 /* [0] R, [1] G, [2] B, [3] A. */
6394 aData.resize(cbData);
6395 for (uint32_t i = 0; i < cbData; i += 4)
6396 {
6397 aData[i] = pu8Data[i + 2];
6398 aData[i + 1] = pu8Data[i + 1];
6399 aData[i + 2] = pu8Data[i];
6400 aData[i + 3] = 0xff;
6401 }
6402 }
6403 else if (aBitmapFormat == BitmapFormat_PNG)
6404 {
6405 uint8_t *pu8PNG = NULL;
6406 uint32_t cbPNG = 0;
6407 uint32_t cxPNG = 0;
6408 uint32_t cyPNG = 0;
6409
6410 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6411
6412 if (RT_SUCCESS(vrc))
6413 {
6414 aData.resize(cbPNG);
6415 if (cbPNG)
6416 memcpy(&aData.front(), pu8PNG, cbPNG);
6417 }
6418 else
6419 hr = setError(VBOX_E_IPRT_ERROR,
6420 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6421 vrc);
6422
6423 RTMemFree(pu8PNG);
6424 }
6425 }
6426
6427 freeSavedDisplayScreenshot(pu8Data);
6428
6429 return hr;
6430}
6431
6432HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6433 ULONG *aWidth,
6434 ULONG *aHeight,
6435 std::vector<BitmapFormat_T> &aBitmapFormats)
6436{
6437 if (aScreenId != 0)
6438 return E_NOTIMPL;
6439
6440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6441
6442 uint8_t *pu8Data = NULL;
6443 uint32_t cbData = 0;
6444 uint32_t u32Width = 0;
6445 uint32_t u32Height = 0;
6446
6447 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6448
6449 if (RT_FAILURE(vrc))
6450 return setError(VBOX_E_IPRT_ERROR,
6451 tr("Saved screenshot data is not available (%Rrc)"),
6452 vrc);
6453
6454 *aWidth = u32Width;
6455 *aHeight = u32Height;
6456 aBitmapFormats.resize(1);
6457 aBitmapFormats[0] = BitmapFormat_PNG;
6458
6459 freeSavedDisplayScreenshot(pu8Data);
6460
6461 return S_OK;
6462}
6463
6464HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6465 BitmapFormat_T aBitmapFormat,
6466 ULONG *aWidth,
6467 ULONG *aHeight,
6468 std::vector<BYTE> &aData)
6469{
6470 if (aScreenId != 0)
6471 return E_NOTIMPL;
6472
6473 if (aBitmapFormat != BitmapFormat_PNG)
6474 return E_NOTIMPL;
6475
6476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6477
6478 uint8_t *pu8Data = NULL;
6479 uint32_t cbData = 0;
6480 uint32_t u32Width = 0;
6481 uint32_t u32Height = 0;
6482
6483 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6484
6485 if (RT_FAILURE(vrc))
6486 return setError(VBOX_E_IPRT_ERROR,
6487 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6488 vrc);
6489
6490 *aWidth = u32Width;
6491 *aHeight = u32Height;
6492
6493 aData.resize(cbData);
6494 if (cbData)
6495 memcpy(&aData.front(), pu8Data, cbData);
6496
6497 freeSavedDisplayScreenshot(pu8Data);
6498
6499 return S_OK;
6500}
6501
6502HRESULT Machine::hotPlugCPU(ULONG aCpu)
6503{
6504 HRESULT rc = S_OK;
6505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6506
6507 if (!mHWData->mCPUHotPlugEnabled)
6508 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6509
6510 if (aCpu >= mHWData->mCPUCount)
6511 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6512
6513 if (mHWData->mCPUAttached[aCpu])
6514 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6515
6516 alock.release();
6517 rc = i_onCPUChange(aCpu, false);
6518 alock.acquire();
6519 if (FAILED(rc)) return rc;
6520
6521 i_setModified(IsModified_MachineData);
6522 mHWData.backup();
6523 mHWData->mCPUAttached[aCpu] = true;
6524
6525 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6526 if (Global::IsOnline(mData->mMachineState))
6527 i_saveSettings(NULL);
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6533{
6534 HRESULT rc = S_OK;
6535
6536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6537
6538 if (!mHWData->mCPUHotPlugEnabled)
6539 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6540
6541 if (aCpu >= SchemaDefs::MaxCPUCount)
6542 return setError(E_INVALIDARG,
6543 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6544 SchemaDefs::MaxCPUCount);
6545
6546 if (!mHWData->mCPUAttached[aCpu])
6547 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6548
6549 /* CPU 0 can't be detached */
6550 if (aCpu == 0)
6551 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6552
6553 alock.release();
6554 rc = i_onCPUChange(aCpu, true);
6555 alock.acquire();
6556 if (FAILED(rc)) return rc;
6557
6558 i_setModified(IsModified_MachineData);
6559 mHWData.backup();
6560 mHWData->mCPUAttached[aCpu] = false;
6561
6562 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6563 if (Global::IsOnline(mData->mMachineState))
6564 i_saveSettings(NULL);
6565
6566 return S_OK;
6567}
6568
6569HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6570{
6571 *aAttached = false;
6572
6573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6574
6575 /* If hotplug is enabled the CPU is always enabled. */
6576 if (!mHWData->mCPUHotPlugEnabled)
6577 {
6578 if (aCpu < mHWData->mCPUCount)
6579 *aAttached = true;
6580 }
6581 else
6582 {
6583 if (aCpu < SchemaDefs::MaxCPUCount)
6584 *aAttached = mHWData->mCPUAttached[aCpu];
6585 }
6586
6587 return S_OK;
6588}
6589
6590HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6591{
6592 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6593
6594 Utf8Str log = i_getLogFilename(aIdx);
6595 if (!RTFileExists(log.c_str()))
6596 log.setNull();
6597 aFilename = log;
6598
6599 return S_OK;
6600}
6601
6602HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6603{
6604 if (aSize < 0)
6605 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6606
6607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6608
6609 HRESULT rc = S_OK;
6610 Utf8Str log = i_getLogFilename(aIdx);
6611
6612 /* do not unnecessarily hold the lock while doing something which does
6613 * not need the lock and potentially takes a long time. */
6614 alock.release();
6615
6616 /* Limit the chunk size to 32K for now, as that gives better performance
6617 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6618 * One byte expands to approx. 25 bytes of breathtaking XML. */
6619 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6620 aData.resize(cbData);
6621
6622 RTFILE LogFile;
6623 int vrc = RTFileOpen(&LogFile, log.c_str(),
6624 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6625 if (RT_SUCCESS(vrc))
6626 {
6627 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6628 if (RT_SUCCESS(vrc))
6629 aData.resize(cbData);
6630 else
6631 rc = setError(VBOX_E_IPRT_ERROR,
6632 tr("Could not read log file '%s' (%Rrc)"),
6633 log.c_str(), vrc);
6634 RTFileClose(LogFile);
6635 }
6636 else
6637 rc = setError(VBOX_E_IPRT_ERROR,
6638 tr("Could not open log file '%s' (%Rrc)"),
6639 log.c_str(), vrc);
6640
6641 if (FAILED(rc))
6642 aData.resize(0);
6643
6644 return rc;
6645}
6646
6647
6648/**
6649 * Currently this method doesn't attach device to the running VM,
6650 * just makes sure it's plugged on next VM start.
6651 */
6652HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6653{
6654 // lock scope
6655 {
6656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6657
6658 HRESULT rc = i_checkStateDependency(MutableStateDep);
6659 if (FAILED(rc)) return rc;
6660
6661 ChipsetType_T aChipset = ChipsetType_PIIX3;
6662 COMGETTER(ChipsetType)(&aChipset);
6663
6664 if (aChipset != ChipsetType_ICH9)
6665 {
6666 return setError(E_INVALIDARG,
6667 tr("Host PCI attachment only supported with ICH9 chipset"));
6668 }
6669
6670 // check if device with this host PCI address already attached
6671 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6672 it != mHWData->mPCIDeviceAssignments.end();
6673 ++it)
6674 {
6675 LONG iHostAddress = -1;
6676 ComPtr<PCIDeviceAttachment> pAttach;
6677 pAttach = *it;
6678 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6679 if (iHostAddress == aHostAddress)
6680 return setError(E_INVALIDARG,
6681 tr("Device with host PCI address already attached to this VM"));
6682 }
6683
6684 ComObjPtr<PCIDeviceAttachment> pda;
6685 char name[32];
6686
6687 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6688 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6689 Bstr bname(name);
6690 pda.createObject();
6691 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6692 i_setModified(IsModified_MachineData);
6693 mHWData.backup();
6694 mHWData->mPCIDeviceAssignments.push_back(pda);
6695 }
6696
6697 return S_OK;
6698}
6699
6700/**
6701 * Currently this method doesn't detach device from the running VM,
6702 * just makes sure it's not plugged on next VM start.
6703 */
6704HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6705{
6706 ComObjPtr<PCIDeviceAttachment> pAttach;
6707 bool fRemoved = false;
6708 HRESULT rc;
6709
6710 // lock scope
6711 {
6712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6713
6714 rc = i_checkStateDependency(MutableStateDep);
6715 if (FAILED(rc)) return rc;
6716
6717 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6718 it != mHWData->mPCIDeviceAssignments.end();
6719 ++it)
6720 {
6721 LONG iHostAddress = -1;
6722 pAttach = *it;
6723 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6724 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6725 {
6726 i_setModified(IsModified_MachineData);
6727 mHWData.backup();
6728 mHWData->mPCIDeviceAssignments.remove(pAttach);
6729 fRemoved = true;
6730 break;
6731 }
6732 }
6733 }
6734
6735
6736 /* Fire event outside of the lock */
6737 if (fRemoved)
6738 {
6739 Assert(!pAttach.isNull());
6740 ComPtr<IEventSource> es;
6741 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6742 Assert(SUCCEEDED(rc));
6743 Bstr mid;
6744 rc = this->COMGETTER(Id)(mid.asOutParam());
6745 Assert(SUCCEEDED(rc));
6746 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6747 }
6748
6749 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6750 tr("No host PCI device %08x attached"),
6751 aHostAddress
6752 );
6753}
6754
6755HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6756{
6757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6758
6759 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6760
6761 size_t i = 0;
6762 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6763 it != mHWData->mPCIDeviceAssignments.end();
6764 ++i, ++it)
6765 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6766
6767 return S_OK;
6768}
6769
6770HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6771{
6772 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6773
6774 return S_OK;
6775}
6776
6777HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6778{
6779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6780
6781 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6782
6783 return S_OK;
6784}
6785
6786HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6787{
6788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6789 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6790 if (SUCCEEDED(hrc))
6791 {
6792 hrc = mHWData.backupEx();
6793 if (SUCCEEDED(hrc))
6794 {
6795 i_setModified(IsModified_MachineData);
6796 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6797 }
6798 }
6799 return hrc;
6800}
6801
6802HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6803{
6804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6805 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6806 return S_OK;
6807}
6808
6809HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6810{
6811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6812 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6813 if (SUCCEEDED(hrc))
6814 {
6815 hrc = mHWData.backupEx();
6816 if (SUCCEEDED(hrc))
6817 {
6818 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6819 if (SUCCEEDED(hrc))
6820 i_setModified(IsModified_MachineData);
6821 }
6822 }
6823 return hrc;
6824}
6825
6826HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6827{
6828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6831
6832 return S_OK;
6833}
6834
6835HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6836{
6837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6839 if (SUCCEEDED(hrc))
6840 {
6841 hrc = mHWData.backupEx();
6842 if (SUCCEEDED(hrc))
6843 {
6844 i_setModified(IsModified_MachineData);
6845 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6846 }
6847 }
6848 return hrc;
6849}
6850
6851HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6852{
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854
6855 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6856
6857 return S_OK;
6858}
6859
6860HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6861{
6862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6863
6864 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6865 if ( SUCCEEDED(hrc)
6866 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6867 {
6868 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6869 int vrc;
6870
6871 if (aAutostartEnabled)
6872 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6873 else
6874 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6875
6876 if (RT_SUCCESS(vrc))
6877 {
6878 hrc = mHWData.backupEx();
6879 if (SUCCEEDED(hrc))
6880 {
6881 i_setModified(IsModified_MachineData);
6882 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6883 }
6884 }
6885 else if (vrc == VERR_NOT_SUPPORTED)
6886 hrc = setError(VBOX_E_NOT_SUPPORTED,
6887 tr("The VM autostart feature is not supported on this platform"));
6888 else if (vrc == VERR_PATH_NOT_FOUND)
6889 hrc = setError(E_FAIL,
6890 tr("The path to the autostart database is not set"));
6891 else
6892 hrc = setError(E_UNEXPECTED,
6893 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6894 aAutostartEnabled ? "Adding" : "Removing",
6895 mUserData->s.strName.c_str(), vrc);
6896 }
6897 return hrc;
6898}
6899
6900HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6901{
6902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6903
6904 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6905
6906 return S_OK;
6907}
6908
6909HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6910{
6911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6912 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6913 if (SUCCEEDED(hrc))
6914 {
6915 hrc = mHWData.backupEx();
6916 if (SUCCEEDED(hrc))
6917 {
6918 i_setModified(IsModified_MachineData);
6919 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6920 }
6921 }
6922 return hrc;
6923}
6924
6925HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6926{
6927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6928
6929 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6930
6931 return S_OK;
6932}
6933
6934HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6935{
6936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6937 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6938 if ( SUCCEEDED(hrc)
6939 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6940 {
6941 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6942 int vrc;
6943
6944 if (aAutostopType != AutostopType_Disabled)
6945 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6946 else
6947 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6948
6949 if (RT_SUCCESS(vrc))
6950 {
6951 hrc = mHWData.backupEx();
6952 if (SUCCEEDED(hrc))
6953 {
6954 i_setModified(IsModified_MachineData);
6955 mHWData->mAutostart.enmAutostopType = aAutostopType;
6956 }
6957 }
6958 else if (vrc == VERR_NOT_SUPPORTED)
6959 hrc = setError(VBOX_E_NOT_SUPPORTED,
6960 tr("The VM autostop feature is not supported on this platform"));
6961 else if (vrc == VERR_PATH_NOT_FOUND)
6962 hrc = setError(E_FAIL,
6963 tr("The path to the autostart database is not set"));
6964 else
6965 hrc = setError(E_UNEXPECTED,
6966 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6967 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6968 mUserData->s.strName.c_str(), vrc);
6969 }
6970 return hrc;
6971}
6972
6973HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6974{
6975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6976
6977 aDefaultFrontend = mHWData->mDefaultFrontend;
6978
6979 return S_OK;
6980}
6981
6982HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6983{
6984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6985 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6986 if (SUCCEEDED(hrc))
6987 {
6988 hrc = mHWData.backupEx();
6989 if (SUCCEEDED(hrc))
6990 {
6991 i_setModified(IsModified_MachineData);
6992 mHWData->mDefaultFrontend = aDefaultFrontend;
6993 }
6994 }
6995 return hrc;
6996}
6997
6998HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6999{
7000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7001 size_t cbIcon = mUserData->mIcon.size();
7002 aIcon.resize(cbIcon);
7003 if (cbIcon)
7004 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7005 return S_OK;
7006}
7007
7008HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7009{
7010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7011 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7012 if (SUCCEEDED(hrc))
7013 {
7014 i_setModified(IsModified_MachineData);
7015 mUserData.backup();
7016 size_t cbIcon = aIcon.size();
7017 mUserData->mIcon.resize(cbIcon);
7018 if (cbIcon)
7019 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7020 }
7021 return hrc;
7022}
7023
7024HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7025{
7026#ifdef VBOX_WITH_USB
7027 *aUSBProxyAvailable = true;
7028#else
7029 *aUSBProxyAvailable = false;
7030#endif
7031 return S_OK;
7032}
7033
7034HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7035{
7036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7037
7038 aVMProcessPriority = mUserData->s.strVMPriority;
7039
7040 return S_OK;
7041}
7042
7043HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7044{
7045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7046 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7047 if (SUCCEEDED(hrc))
7048 {
7049 /** @todo r=klaus: currently this is marked as not implemented, as
7050 * the code for setting the priority of the process is not there
7051 * (neither when starting the VM nor at runtime). */
7052 ReturnComNotImplemented();
7053 hrc = mUserData.backupEx();
7054 if (SUCCEEDED(hrc))
7055 {
7056 i_setModified(IsModified_MachineData);
7057 mUserData->s.strVMPriority = aVMProcessPriority;
7058 }
7059 }
7060 return hrc;
7061}
7062
7063HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7064 ComPtr<IProgress> &aProgress)
7065{
7066 ComObjPtr<Progress> pP;
7067 Progress *ppP = pP;
7068 IProgress *iP = static_cast<IProgress *>(ppP);
7069 IProgress **pProgress = &iP;
7070
7071 IMachine *pTarget = aTarget;
7072
7073 /* Convert the options. */
7074 RTCList<CloneOptions_T> optList;
7075 if (aOptions.size())
7076 for (size_t i = 0; i < aOptions.size(); ++i)
7077 optList.append(aOptions[i]);
7078
7079 if (optList.contains(CloneOptions_Link))
7080 {
7081 if (!i_isSnapshotMachine())
7082 return setError(E_INVALIDARG,
7083 tr("Linked clone can only be created from a snapshot"));
7084 if (aMode != CloneMode_MachineState)
7085 return setError(E_INVALIDARG,
7086 tr("Linked clone can only be created for a single machine state"));
7087 }
7088 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7089
7090 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7091
7092 HRESULT rc = pWorker->start(pProgress);
7093
7094 pP = static_cast<Progress *>(*pProgress);
7095 pP.queryInterfaceTo(aProgress.asOutParam());
7096
7097 return rc;
7098
7099}
7100
7101HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7102{
7103 NOREF(aProgress);
7104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7105
7106 // This check should always fail.
7107 HRESULT rc = i_checkStateDependency(MutableStateDep);
7108 if (FAILED(rc)) return rc;
7109
7110 AssertFailedReturn(E_NOTIMPL);
7111}
7112
7113HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7114{
7115 NOREF(aSavedStateFile);
7116 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7117
7118 // This check should always fail.
7119 HRESULT rc = i_checkStateDependency(MutableStateDep);
7120 if (FAILED(rc)) return rc;
7121
7122 AssertFailedReturn(E_NOTIMPL);
7123}
7124
7125HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7126{
7127 NOREF(aFRemoveFile);
7128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7129
7130 // This check should always fail.
7131 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7132 if (FAILED(rc)) return rc;
7133
7134 AssertFailedReturn(E_NOTIMPL);
7135}
7136
7137// public methods for internal purposes
7138/////////////////////////////////////////////////////////////////////////////
7139
7140/**
7141 * Adds the given IsModified_* flag to the dirty flags of the machine.
7142 * This must be called either during i_loadSettings or under the machine write lock.
7143 * @param fl
7144 */
7145void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7146{
7147 mData->flModifications |= fl;
7148 if (fAllowStateModification && i_isStateModificationAllowed())
7149 mData->mCurrentStateModified = true;
7150}
7151
7152/**
7153 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7154 * care of the write locking.
7155 *
7156 * @param fModifications The flag to add.
7157 */
7158void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7159{
7160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7161 i_setModified(fModification, fAllowStateModification);
7162}
7163
7164/**
7165 * Saves the registry entry of this machine to the given configuration node.
7166 *
7167 * @param aEntryNode Node to save the registry entry to.
7168 *
7169 * @note locks this object for reading.
7170 */
7171HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7172{
7173 AutoLimitedCaller autoCaller(this);
7174 AssertComRCReturnRC(autoCaller.rc());
7175
7176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7177
7178 data.uuid = mData->mUuid;
7179 data.strSettingsFile = mData->m_strConfigFile;
7180
7181 return S_OK;
7182}
7183
7184/**
7185 * Calculates the absolute path of the given path taking the directory of the
7186 * machine settings file as the current directory.
7187 *
7188 * @param aPath Path to calculate the absolute path for.
7189 * @param aResult Where to put the result (used only on success, can be the
7190 * same Utf8Str instance as passed in @a aPath).
7191 * @return IPRT result.
7192 *
7193 * @note Locks this object for reading.
7194 */
7195int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7196{
7197 AutoCaller autoCaller(this);
7198 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7199
7200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7201
7202 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7203
7204 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7205
7206 strSettingsDir.stripFilename();
7207 char folder[RTPATH_MAX];
7208 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7209 if (RT_SUCCESS(vrc))
7210 aResult = folder;
7211
7212 return vrc;
7213}
7214
7215/**
7216 * Copies strSource to strTarget, making it relative to the machine folder
7217 * if it is a subdirectory thereof, or simply copying it otherwise.
7218 *
7219 * @param strSource Path to evaluate and copy.
7220 * @param strTarget Buffer to receive target path.
7221 *
7222 * @note Locks this object for reading.
7223 */
7224void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7225 Utf8Str &strTarget)
7226{
7227 AutoCaller autoCaller(this);
7228 AssertComRCReturn(autoCaller.rc(), (void)0);
7229
7230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7231
7232 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7233 // use strTarget as a temporary buffer to hold the machine settings dir
7234 strTarget = mData->m_strConfigFileFull;
7235 strTarget.stripFilename();
7236 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7237 {
7238 // is relative: then append what's left
7239 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7240 // for empty paths (only possible for subdirs) use "." to avoid
7241 // triggering default settings for not present config attributes.
7242 if (strTarget.isEmpty())
7243 strTarget = ".";
7244 }
7245 else
7246 // is not relative: then overwrite
7247 strTarget = strSource;
7248}
7249
7250/**
7251 * Returns the full path to the machine's log folder in the
7252 * \a aLogFolder argument.
7253 */
7254void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7255{
7256 AutoCaller autoCaller(this);
7257 AssertComRCReturnVoid(autoCaller.rc());
7258
7259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7260
7261 char szTmp[RTPATH_MAX];
7262 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7263 if (RT_SUCCESS(vrc))
7264 {
7265 if (szTmp[0] && !mUserData.isNull())
7266 {
7267 char szTmp2[RTPATH_MAX];
7268 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7269 if (RT_SUCCESS(vrc))
7270 aLogFolder = BstrFmt("%s%c%s",
7271 szTmp2,
7272 RTPATH_DELIMITER,
7273 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7274 }
7275 else
7276 vrc = VERR_PATH_IS_RELATIVE;
7277 }
7278
7279 if (RT_FAILURE(vrc))
7280 {
7281 // fallback if VBOX_USER_LOGHOME is not set or invalid
7282 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7283 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7284 aLogFolder.append(RTPATH_DELIMITER);
7285 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7286 }
7287}
7288
7289/**
7290 * Returns the full path to the machine's log file for an given index.
7291 */
7292Utf8Str Machine::i_getLogFilename(ULONG idx)
7293{
7294 Utf8Str logFolder;
7295 getLogFolder(logFolder);
7296 Assert(logFolder.length());
7297
7298 Utf8Str log;
7299 if (idx == 0)
7300 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7301#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7302 else if (idx == 1)
7303 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7304 else
7305 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7306#else
7307 else
7308 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7309#endif
7310 return log;
7311}
7312
7313/**
7314 * Returns the full path to the machine's hardened log file.
7315 */
7316Utf8Str Machine::i_getHardeningLogFilename(void)
7317{
7318 Utf8Str strFilename;
7319 getLogFolder(strFilename);
7320 Assert(strFilename.length());
7321 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7322 return strFilename;
7323}
7324
7325
7326/**
7327 * Composes a unique saved state filename based on the current system time. The filename is
7328 * granular to the second so this will work so long as no more than one snapshot is taken on
7329 * a machine per second.
7330 *
7331 * Before version 4.1, we used this formula for saved state files:
7332 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7333 * which no longer works because saved state files can now be shared between the saved state of the
7334 * "saved" machine and an online snapshot, and the following would cause problems:
7335 * 1) save machine
7336 * 2) create online snapshot from that machine state --> reusing saved state file
7337 * 3) save machine again --> filename would be reused, breaking the online snapshot
7338 *
7339 * So instead we now use a timestamp.
7340 *
7341 * @param str
7342 */
7343
7344void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7345{
7346 AutoCaller autoCaller(this);
7347 AssertComRCReturnVoid(autoCaller.rc());
7348
7349 {
7350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7351 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7352 }
7353
7354 RTTIMESPEC ts;
7355 RTTimeNow(&ts);
7356 RTTIME time;
7357 RTTimeExplode(&time, &ts);
7358
7359 strStateFilePath += RTPATH_DELIMITER;
7360 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7361 time.i32Year, time.u8Month, time.u8MonthDay,
7362 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7363}
7364
7365/**
7366 * Returns the full path to the default video capture file.
7367 */
7368void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7369{
7370 AutoCaller autoCaller(this);
7371 AssertComRCReturnVoid(autoCaller.rc());
7372
7373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7374
7375 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7376 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7377 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7378}
7379
7380/**
7381 * Returns whether at least one USB controller is present for the VM.
7382 */
7383bool Machine::i_isUSBControllerPresent()
7384{
7385 AutoCaller autoCaller(this);
7386 AssertComRCReturn(autoCaller.rc(), false);
7387
7388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7389
7390 return (mUSBControllers->size() > 0);
7391}
7392
7393/**
7394 * @note Locks this object for writing, calls the client process
7395 * (inside the lock).
7396 */
7397HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7398 const Utf8Str &strFrontend,
7399 const Utf8Str &strEnvironment,
7400 ProgressProxy *aProgress)
7401{
7402 LogFlowThisFuncEnter();
7403
7404 AssertReturn(aControl, E_FAIL);
7405 AssertReturn(aProgress, E_FAIL);
7406 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7407
7408 AutoCaller autoCaller(this);
7409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7410
7411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7412
7413 if (!mData->mRegistered)
7414 return setError(E_UNEXPECTED,
7415 tr("The machine '%s' is not registered"),
7416 mUserData->s.strName.c_str());
7417
7418 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7419
7420 /* The process started when launching a VM with separate UI/VM processes is always
7421 * the UI process, i.e. needs special handling as it won't claim the session. */
7422 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7423
7424 if (fSeparate)
7425 {
7426 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7427 return setError(VBOX_E_INVALID_OBJECT_STATE,
7428 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7429 mUserData->s.strName.c_str());
7430 }
7431 else
7432 {
7433 if ( mData->mSession.mState == SessionState_Locked
7434 || mData->mSession.mState == SessionState_Spawning
7435 || mData->mSession.mState == SessionState_Unlocking)
7436 return setError(VBOX_E_INVALID_OBJECT_STATE,
7437 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7438 mUserData->s.strName.c_str());
7439
7440 /* may not be busy */
7441 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7442 }
7443
7444 /* get the path to the executable */
7445 char szPath[RTPATH_MAX];
7446 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7447 size_t cchBufLeft = strlen(szPath);
7448 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7449 szPath[cchBufLeft] = 0;
7450 char *pszNamePart = szPath + cchBufLeft;
7451 cchBufLeft = sizeof(szPath) - cchBufLeft;
7452
7453 int vrc = VINF_SUCCESS;
7454 RTPROCESS pid = NIL_RTPROCESS;
7455
7456 RTENV env = RTENV_DEFAULT;
7457
7458 if (!strEnvironment.isEmpty())
7459 {
7460 char *newEnvStr = NULL;
7461
7462 do
7463 {
7464 /* clone the current environment */
7465 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7466 AssertRCBreakStmt(vrc2, vrc = vrc2);
7467
7468 newEnvStr = RTStrDup(strEnvironment.c_str());
7469 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7470
7471 /* put new variables to the environment
7472 * (ignore empty variable names here since RTEnv API
7473 * intentionally doesn't do that) */
7474 char *var = newEnvStr;
7475 for (char *p = newEnvStr; *p; ++p)
7476 {
7477 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7478 {
7479 *p = '\0';
7480 if (*var)
7481 {
7482 char *val = strchr(var, '=');
7483 if (val)
7484 {
7485 *val++ = '\0';
7486 vrc2 = RTEnvSetEx(env, var, val);
7487 }
7488 else
7489 vrc2 = RTEnvUnsetEx(env, var);
7490 if (RT_FAILURE(vrc2))
7491 break;
7492 }
7493 var = p + 1;
7494 }
7495 }
7496 if (RT_SUCCESS(vrc2) && *var)
7497 vrc2 = RTEnvPutEx(env, var);
7498
7499 AssertRCBreakStmt(vrc2, vrc = vrc2);
7500 }
7501 while (0);
7502
7503 if (newEnvStr != NULL)
7504 RTStrFree(newEnvStr);
7505 }
7506
7507 /* Hardening logging */
7508#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7509 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7510 {
7511 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7512 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7513 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7514 {
7515 Utf8Str strStartupLogDir = strHardeningLogFile;
7516 strStartupLogDir.stripFilename();
7517 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7518 file without stripping the file. */
7519 }
7520 strSupHardeningLogArg.append(strHardeningLogFile);
7521
7522 /* Remove legacy log filename to avoid confusion. */
7523 Utf8Str strOldStartupLogFile;
7524 getLogFolder(strOldStartupLogFile);
7525 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7526 RTFileDelete(strOldStartupLogFile.c_str());
7527 }
7528 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7529#else
7530 const char *pszSupHardeningLogArg = NULL;
7531#endif
7532
7533 Utf8Str strCanonicalName;
7534
7535#ifdef VBOX_WITH_QTGUI
7536 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7537 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7538 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7539 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7540 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7541 {
7542 strCanonicalName = "GUI/Qt";
7543# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7544 /* Modify the base path so that we don't need to use ".." below. */
7545 RTPathStripTrailingSlash(szPath);
7546 RTPathStripFilename(szPath);
7547 cchBufLeft = strlen(szPath);
7548 pszNamePart = szPath + cchBufLeft;
7549 cchBufLeft = sizeof(szPath) - cchBufLeft;
7550
7551# define OSX_APP_NAME "VirtualBoxVM"
7552# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7553
7554 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7555 if ( strAppOverride.contains(".")
7556 || strAppOverride.contains("/")
7557 || strAppOverride.contains("\\")
7558 || strAppOverride.contains(":"))
7559 strAppOverride.setNull();
7560 Utf8Str strAppPath;
7561 if (!strAppOverride.isEmpty())
7562 {
7563 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7564 Utf8Str strFullPath(szPath);
7565 strFullPath.append(strAppPath);
7566 /* there is a race, but people using this deserve the failure */
7567 if (!RTFileExists(strFullPath.c_str()))
7568 strAppOverride.setNull();
7569 }
7570 if (strAppOverride.isEmpty())
7571 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7572 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7573 strcpy(pszNamePart, strAppPath.c_str());
7574# else
7575 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7576 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7577 strcpy(pszNamePart, s_szVirtualBox_exe);
7578# endif
7579
7580 Utf8Str idStr = mData->mUuid.toString();
7581 const char *apszArgs[] =
7582 {
7583 szPath,
7584 "--comment", mUserData->s.strName.c_str(),
7585 "--startvm", idStr.c_str(),
7586 "--no-startvm-errormsgbox",
7587 NULL, /* For "--separate". */
7588 NULL, /* For "--sup-startup-log". */
7589 NULL
7590 };
7591 unsigned iArg = 6;
7592 if (fSeparate)
7593 apszArgs[iArg++] = "--separate";
7594 apszArgs[iArg++] = pszSupHardeningLogArg;
7595
7596 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7597 }
7598#else /* !VBOX_WITH_QTGUI */
7599 if (0)
7600 ;
7601#endif /* VBOX_WITH_QTGUI */
7602
7603 else
7604
7605#ifdef VBOX_WITH_VBOXSDL
7606 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7607 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7608 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7609 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7610 {
7611 strCanonicalName = "GUI/SDL";
7612 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7613 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7614 strcpy(pszNamePart, s_szVBoxSDL_exe);
7615
7616 Utf8Str idStr = mData->mUuid.toString();
7617 const char *apszArgs[] =
7618 {
7619 szPath,
7620 "--comment", mUserData->s.strName.c_str(),
7621 "--startvm", idStr.c_str(),
7622 NULL, /* For "--separate". */
7623 NULL, /* For "--sup-startup-log". */
7624 NULL
7625 };
7626 unsigned iArg = 5;
7627 if (fSeparate)
7628 apszArgs[iArg++] = "--separate";
7629 apszArgs[iArg++] = pszSupHardeningLogArg;
7630
7631 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7632 }
7633#else /* !VBOX_WITH_VBOXSDL */
7634 if (0)
7635 ;
7636#endif /* !VBOX_WITH_VBOXSDL */
7637
7638 else
7639
7640#ifdef VBOX_WITH_HEADLESS
7641 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7642 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7643 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7644 )
7645 {
7646 strCanonicalName = "headless";
7647 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7648 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7649 * and a VM works even if the server has not been installed.
7650 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7651 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7652 * differently in 4.0 and 3.x.
7653 */
7654 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7655 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7656 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7657
7658 Utf8Str idStr = mData->mUuid.toString();
7659 const char *apszArgs[] =
7660 {
7661 szPath,
7662 "--comment", mUserData->s.strName.c_str(),
7663 "--startvm", idStr.c_str(),
7664 "--vrde", "config",
7665 NULL, /* For "--capture". */
7666 NULL, /* For "--sup-startup-log". */
7667 NULL
7668 };
7669 unsigned iArg = 7;
7670 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7671 apszArgs[iArg++] = "--capture";
7672 apszArgs[iArg++] = pszSupHardeningLogArg;
7673
7674# ifdef RT_OS_WINDOWS
7675 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7676# else
7677 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7678# endif
7679 }
7680#else /* !VBOX_WITH_HEADLESS */
7681 if (0)
7682 ;
7683#endif /* !VBOX_WITH_HEADLESS */
7684 else
7685 {
7686 RTEnvDestroy(env);
7687 return setError(E_INVALIDARG,
7688 tr("Invalid frontend name: '%s'"),
7689 strFrontend.c_str());
7690 }
7691
7692 RTEnvDestroy(env);
7693
7694 if (RT_FAILURE(vrc))
7695 return setError(VBOX_E_IPRT_ERROR,
7696 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7697 mUserData->s.strName.c_str(), vrc);
7698
7699 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7700
7701 if (!fSeparate)
7702 {
7703 /*
7704 * Note that we don't release the lock here before calling the client,
7705 * because it doesn't need to call us back if called with a NULL argument.
7706 * Releasing the lock here is dangerous because we didn't prepare the
7707 * launch data yet, but the client we've just started may happen to be
7708 * too fast and call LockMachine() that will fail (because of PID, etc.),
7709 * so that the Machine will never get out of the Spawning session state.
7710 */
7711
7712 /* inform the session that it will be a remote one */
7713 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7714#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7715 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7716#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7717 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7718#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7719 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7720
7721 if (FAILED(rc))
7722 {
7723 /* restore the session state */
7724 mData->mSession.mState = SessionState_Unlocked;
7725 alock.release();
7726 mParent->i_addProcessToReap(pid);
7727 /* The failure may occur w/o any error info (from RPC), so provide one */
7728 return setError(VBOX_E_VM_ERROR,
7729 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7730 }
7731
7732 /* attach launch data to the machine */
7733 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7734 mData->mSession.mRemoteControls.push_back(aControl);
7735 mData->mSession.mProgress = aProgress;
7736 mData->mSession.mPID = pid;
7737 mData->mSession.mState = SessionState_Spawning;
7738 Assert(strCanonicalName.isNotEmpty());
7739 mData->mSession.mName = strCanonicalName;
7740 }
7741 else
7742 {
7743 /* For separate UI process we declare the launch as completed instantly, as the
7744 * actual headless VM start may or may not come. No point in remembering anything
7745 * yet, as what matters for us is when the headless VM gets started. */
7746 aProgress->i_notifyComplete(S_OK);
7747 }
7748
7749 alock.release();
7750 mParent->i_addProcessToReap(pid);
7751
7752 LogFlowThisFuncLeave();
7753 return S_OK;
7754}
7755
7756/**
7757 * Returns @c true if the given session machine instance has an open direct
7758 * session (and optionally also for direct sessions which are closing) and
7759 * returns the session control machine instance if so.
7760 *
7761 * Note that when the method returns @c false, the arguments remain unchanged.
7762 *
7763 * @param aMachine Session machine object.
7764 * @param aControl Direct session control object (optional).
7765 * @param aRequireVM If true then only allow VM sessions.
7766 * @param aAllowClosing If true then additionally a session which is currently
7767 * being closed will also be allowed.
7768 *
7769 * @note locks this object for reading.
7770 */
7771bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7772 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7773 bool aRequireVM /*= false*/,
7774 bool aAllowClosing /*= false*/)
7775{
7776 AutoLimitedCaller autoCaller(this);
7777 AssertComRCReturn(autoCaller.rc(), false);
7778
7779 /* just return false for inaccessible machines */
7780 if (getObjectState().getState() != ObjectState::Ready)
7781 return false;
7782
7783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 if ( ( mData->mSession.mState == SessionState_Locked
7786 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7787 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7788 )
7789 {
7790 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7791
7792 aMachine = mData->mSession.mMachine;
7793
7794 if (aControl != NULL)
7795 *aControl = mData->mSession.mDirectControl;
7796
7797 return true;
7798 }
7799
7800 return false;
7801}
7802
7803/**
7804 * Returns @c true if the given machine has an spawning direct session.
7805 *
7806 * @note locks this object for reading.
7807 */
7808bool Machine::i_isSessionSpawning()
7809{
7810 AutoLimitedCaller autoCaller(this);
7811 AssertComRCReturn(autoCaller.rc(), false);
7812
7813 /* just return false for inaccessible machines */
7814 if (getObjectState().getState() != ObjectState::Ready)
7815 return false;
7816
7817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7818
7819 if (mData->mSession.mState == SessionState_Spawning)
7820 return true;
7821
7822 return false;
7823}
7824
7825/**
7826 * Called from the client watcher thread to check for unexpected client process
7827 * death during Session_Spawning state (e.g. before it successfully opened a
7828 * direct session).
7829 *
7830 * On Win32 and on OS/2, this method is called only when we've got the
7831 * direct client's process termination notification, so it always returns @c
7832 * true.
7833 *
7834 * On other platforms, this method returns @c true if the client process is
7835 * terminated and @c false if it's still alive.
7836 *
7837 * @note Locks this object for writing.
7838 */
7839bool Machine::i_checkForSpawnFailure()
7840{
7841 AutoCaller autoCaller(this);
7842 if (!autoCaller.isOk())
7843 {
7844 /* nothing to do */
7845 LogFlowThisFunc(("Already uninitialized!\n"));
7846 return true;
7847 }
7848
7849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7850
7851 if (mData->mSession.mState != SessionState_Spawning)
7852 {
7853 /* nothing to do */
7854 LogFlowThisFunc(("Not spawning any more!\n"));
7855 return true;
7856 }
7857
7858 HRESULT rc = S_OK;
7859
7860 /* PID not yet initialized, skip check. */
7861 if (mData->mSession.mPID == NIL_RTPROCESS)
7862 return false;
7863
7864 RTPROCSTATUS status;
7865 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7866
7867 if (vrc != VERR_PROCESS_RUNNING)
7868 {
7869 Utf8Str strExtraInfo;
7870
7871#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7872 /* If the startup logfile exists and is of non-zero length, tell the
7873 user to look there for more details to encourage them to attach it
7874 when reporting startup issues. */
7875 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7876 uint64_t cbStartupLogFile = 0;
7877 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7878 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7879 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7880#endif
7881
7882 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7883 rc = setError(E_FAIL,
7884 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7885 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7886 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7887 rc = setError(E_FAIL,
7888 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7889 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7890 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7891 rc = setError(E_FAIL,
7892 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7893 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7894 else
7895 rc = setError(E_FAIL,
7896 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7897 i_getName().c_str(), vrc, strExtraInfo.c_str());
7898 }
7899
7900 if (FAILED(rc))
7901 {
7902 /* Close the remote session, remove the remote control from the list
7903 * and reset session state to Closed (@note keep the code in sync with
7904 * the relevant part in LockMachine()). */
7905
7906 Assert(mData->mSession.mRemoteControls.size() == 1);
7907 if (mData->mSession.mRemoteControls.size() == 1)
7908 {
7909 ErrorInfoKeeper eik;
7910 mData->mSession.mRemoteControls.front()->Uninitialize();
7911 }
7912
7913 mData->mSession.mRemoteControls.clear();
7914 mData->mSession.mState = SessionState_Unlocked;
7915
7916 /* finalize the progress after setting the state */
7917 if (!mData->mSession.mProgress.isNull())
7918 {
7919 mData->mSession.mProgress->notifyComplete(rc);
7920 mData->mSession.mProgress.setNull();
7921 }
7922
7923 mData->mSession.mPID = NIL_RTPROCESS;
7924
7925 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7926 return true;
7927 }
7928
7929 return false;
7930}
7931
7932/**
7933 * Checks whether the machine can be registered. If so, commits and saves
7934 * all settings.
7935 *
7936 * @note Must be called from mParent's write lock. Locks this object and
7937 * children for writing.
7938 */
7939HRESULT Machine::i_prepareRegister()
7940{
7941 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7942
7943 AutoLimitedCaller autoCaller(this);
7944 AssertComRCReturnRC(autoCaller.rc());
7945
7946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7947
7948 /* wait for state dependents to drop to zero */
7949 i_ensureNoStateDependencies();
7950
7951 if (!mData->mAccessible)
7952 return setError(VBOX_E_INVALID_OBJECT_STATE,
7953 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7954 mUserData->s.strName.c_str(),
7955 mData->mUuid.toString().c_str());
7956
7957 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7958
7959 if (mData->mRegistered)
7960 return setError(VBOX_E_INVALID_OBJECT_STATE,
7961 tr("The machine '%s' with UUID {%s} is already registered"),
7962 mUserData->s.strName.c_str(),
7963 mData->mUuid.toString().c_str());
7964
7965 HRESULT rc = S_OK;
7966
7967 // Ensure the settings are saved. If we are going to be registered and
7968 // no config file exists yet, create it by calling i_saveSettings() too.
7969 if ( (mData->flModifications)
7970 || (!mData->pMachineConfigFile->fileExists())
7971 )
7972 {
7973 rc = i_saveSettings(NULL);
7974 // no need to check whether VirtualBox.xml needs saving too since
7975 // we can't have a machine XML file rename pending
7976 if (FAILED(rc)) return rc;
7977 }
7978
7979 /* more config checking goes here */
7980
7981 if (SUCCEEDED(rc))
7982 {
7983 /* we may have had implicit modifications we want to fix on success */
7984 i_commit();
7985
7986 mData->mRegistered = true;
7987 }
7988 else
7989 {
7990 /* we may have had implicit modifications we want to cancel on failure*/
7991 i_rollback(false /* aNotify */);
7992 }
7993
7994 return rc;
7995}
7996
7997/**
7998 * Increases the number of objects dependent on the machine state or on the
7999 * registered state. Guarantees that these two states will not change at least
8000 * until #releaseStateDependency() is called.
8001 *
8002 * Depending on the @a aDepType value, additional state checks may be made.
8003 * These checks will set extended error info on failure. See
8004 * #checkStateDependency() for more info.
8005 *
8006 * If this method returns a failure, the dependency is not added and the caller
8007 * is not allowed to rely on any particular machine state or registration state
8008 * value and may return the failed result code to the upper level.
8009 *
8010 * @param aDepType Dependency type to add.
8011 * @param aState Current machine state (NULL if not interested).
8012 * @param aRegistered Current registered state (NULL if not interested).
8013 *
8014 * @note Locks this object for writing.
8015 */
8016HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8017 MachineState_T *aState /* = NULL */,
8018 BOOL *aRegistered /* = NULL */)
8019{
8020 AutoCaller autoCaller(this);
8021 AssertComRCReturnRC(autoCaller.rc());
8022
8023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8024
8025 HRESULT rc = i_checkStateDependency(aDepType);
8026 if (FAILED(rc)) return rc;
8027
8028 {
8029 if (mData->mMachineStateChangePending != 0)
8030 {
8031 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8032 * drop to zero so don't add more. It may make sense to wait a bit
8033 * and retry before reporting an error (since the pending state
8034 * transition should be really quick) but let's just assert for
8035 * now to see if it ever happens on practice. */
8036
8037 AssertFailed();
8038
8039 return setError(E_ACCESSDENIED,
8040 tr("Machine state change is in progress. Please retry the operation later."));
8041 }
8042
8043 ++mData->mMachineStateDeps;
8044 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8045 }
8046
8047 if (aState)
8048 *aState = mData->mMachineState;
8049 if (aRegistered)
8050 *aRegistered = mData->mRegistered;
8051
8052 return S_OK;
8053}
8054
8055/**
8056 * Decreases the number of objects dependent on the machine state.
8057 * Must always complete the #addStateDependency() call after the state
8058 * dependency is no more necessary.
8059 */
8060void Machine::i_releaseStateDependency()
8061{
8062 AutoCaller autoCaller(this);
8063 AssertComRCReturnVoid(autoCaller.rc());
8064
8065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8066
8067 /* releaseStateDependency() w/o addStateDependency()? */
8068 AssertReturnVoid(mData->mMachineStateDeps != 0);
8069 -- mData->mMachineStateDeps;
8070
8071 if (mData->mMachineStateDeps == 0)
8072 {
8073 /* inform i_ensureNoStateDependencies() that there are no more deps */
8074 if (mData->mMachineStateChangePending != 0)
8075 {
8076 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8077 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8078 }
8079 }
8080}
8081
8082Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8083{
8084 /* start with nothing found */
8085 Utf8Str strResult("");
8086
8087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8088
8089 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8090 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8091 // found:
8092 strResult = it->second; // source is a Utf8Str
8093
8094 return strResult;
8095}
8096
8097// protected methods
8098/////////////////////////////////////////////////////////////////////////////
8099
8100/**
8101 * Performs machine state checks based on the @a aDepType value. If a check
8102 * fails, this method will set extended error info, otherwise it will return
8103 * S_OK. It is supposed, that on failure, the caller will immediately return
8104 * the return value of this method to the upper level.
8105 *
8106 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8107 *
8108 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8109 * current state of this machine object allows to change settings of the
8110 * machine (i.e. the machine is not registered, or registered but not running
8111 * and not saved). It is useful to call this method from Machine setters
8112 * before performing any change.
8113 *
8114 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8115 * as for MutableStateDep except that if the machine is saved, S_OK is also
8116 * returned. This is useful in setters which allow changing machine
8117 * properties when it is in the saved state.
8118 *
8119 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8120 * if the current state of this machine object allows to change runtime
8121 * changeable settings of the machine (i.e. the machine is not registered, or
8122 * registered but either running or not running and not saved). It is useful
8123 * to call this method from Machine setters before performing any changes to
8124 * runtime changeable settings.
8125 *
8126 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8127 * the same as for MutableOrRunningStateDep except that if the machine is
8128 * saved, S_OK is also returned. This is useful in setters which allow
8129 * changing runtime and saved state changeable machine properties.
8130 *
8131 * @param aDepType Dependency type to check.
8132 *
8133 * @note Non Machine based classes should use #addStateDependency() and
8134 * #releaseStateDependency() methods or the smart AutoStateDependency
8135 * template.
8136 *
8137 * @note This method must be called from under this object's read or write
8138 * lock.
8139 */
8140HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8141{
8142 switch (aDepType)
8143 {
8144 case AnyStateDep:
8145 {
8146 break;
8147 }
8148 case MutableStateDep:
8149 {
8150 if ( mData->mRegistered
8151 && ( !i_isSessionMachine()
8152 || ( mData->mMachineState != MachineState_Aborted
8153 && mData->mMachineState != MachineState_Teleported
8154 && mData->mMachineState != MachineState_PoweredOff
8155 )
8156 )
8157 )
8158 return setError(VBOX_E_INVALID_VM_STATE,
8159 tr("The machine is not mutable (state is %s)"),
8160 Global::stringifyMachineState(mData->mMachineState));
8161 break;
8162 }
8163 case MutableOrSavedStateDep:
8164 {
8165 if ( mData->mRegistered
8166 && ( !i_isSessionMachine()
8167 || ( mData->mMachineState != MachineState_Aborted
8168 && mData->mMachineState != MachineState_Teleported
8169 && mData->mMachineState != MachineState_Saved
8170 && mData->mMachineState != MachineState_PoweredOff
8171 )
8172 )
8173 )
8174 return setError(VBOX_E_INVALID_VM_STATE,
8175 tr("The machine is not mutable or saved (state is %s)"),
8176 Global::stringifyMachineState(mData->mMachineState));
8177 break;
8178 }
8179 case MutableOrRunningStateDep:
8180 {
8181 if ( mData->mRegistered
8182 && ( !i_isSessionMachine()
8183 || ( mData->mMachineState != MachineState_Aborted
8184 && mData->mMachineState != MachineState_Teleported
8185 && mData->mMachineState != MachineState_PoweredOff
8186 && !Global::IsOnline(mData->mMachineState)
8187 )
8188 )
8189 )
8190 return setError(VBOX_E_INVALID_VM_STATE,
8191 tr("The machine is not mutable or running (state is %s)"),
8192 Global::stringifyMachineState(mData->mMachineState));
8193 break;
8194 }
8195 case MutableOrSavedOrRunningStateDep:
8196 {
8197 if ( mData->mRegistered
8198 && ( !i_isSessionMachine()
8199 || ( mData->mMachineState != MachineState_Aborted
8200 && mData->mMachineState != MachineState_Teleported
8201 && mData->mMachineState != MachineState_Saved
8202 && mData->mMachineState != MachineState_PoweredOff
8203 && !Global::IsOnline(mData->mMachineState)
8204 )
8205 )
8206 )
8207 return setError(VBOX_E_INVALID_VM_STATE,
8208 tr("The machine is not mutable, saved or running (state is %s)"),
8209 Global::stringifyMachineState(mData->mMachineState));
8210 break;
8211 }
8212 }
8213
8214 return S_OK;
8215}
8216
8217/**
8218 * Helper to initialize all associated child objects and allocate data
8219 * structures.
8220 *
8221 * This method must be called as a part of the object's initialization procedure
8222 * (usually done in the #init() method).
8223 *
8224 * @note Must be called only from #init() or from #registeredInit().
8225 */
8226HRESULT Machine::initDataAndChildObjects()
8227{
8228 AutoCaller autoCaller(this);
8229 AssertComRCReturnRC(autoCaller.rc());
8230 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8231 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8232
8233 AssertReturn(!mData->mAccessible, E_FAIL);
8234
8235 /* allocate data structures */
8236 mSSData.allocate();
8237 mUserData.allocate();
8238 mHWData.allocate();
8239 mMediaData.allocate();
8240 mStorageControllers.allocate();
8241 mUSBControllers.allocate();
8242
8243 /* initialize mOSTypeId */
8244 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8245
8246 /* create associated BIOS settings object */
8247 unconst(mBIOSSettings).createObject();
8248 mBIOSSettings->init(this);
8249
8250 /* create an associated VRDE object (default is disabled) */
8251 unconst(mVRDEServer).createObject();
8252 mVRDEServer->init(this);
8253
8254 /* create associated serial port objects */
8255 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8256 {
8257 unconst(mSerialPorts[slot]).createObject();
8258 mSerialPorts[slot]->init(this, slot);
8259 }
8260
8261 /* create associated parallel port objects */
8262 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8263 {
8264 unconst(mParallelPorts[slot]).createObject();
8265 mParallelPorts[slot]->init(this, slot);
8266 }
8267
8268 /* create the audio adapter object (always present, default is disabled) */
8269 unconst(mAudioAdapter).createObject();
8270 mAudioAdapter->init(this);
8271
8272 /* create the USB device filters object (always present) */
8273 unconst(mUSBDeviceFilters).createObject();
8274 mUSBDeviceFilters->init(this);
8275
8276 /* create associated network adapter objects */
8277 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8278 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8279 {
8280 unconst(mNetworkAdapters[slot]).createObject();
8281 mNetworkAdapters[slot]->init(this, slot);
8282 }
8283
8284 /* create the bandwidth control */
8285 unconst(mBandwidthControl).createObject();
8286 mBandwidthControl->init(this);
8287
8288 return S_OK;
8289}
8290
8291/**
8292 * Helper to uninitialize all associated child objects and to free all data
8293 * structures.
8294 *
8295 * This method must be called as a part of the object's uninitialization
8296 * procedure (usually done in the #uninit() method).
8297 *
8298 * @note Must be called only from #uninit() or from #registeredInit().
8299 */
8300void Machine::uninitDataAndChildObjects()
8301{
8302 AutoCaller autoCaller(this);
8303 AssertComRCReturnVoid(autoCaller.rc());
8304 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8305 || getObjectState().getState() == ObjectState::Limited);
8306
8307 /* tell all our other child objects we've been uninitialized */
8308 if (mBandwidthControl)
8309 {
8310 mBandwidthControl->uninit();
8311 unconst(mBandwidthControl).setNull();
8312 }
8313
8314 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8315 {
8316 if (mNetworkAdapters[slot])
8317 {
8318 mNetworkAdapters[slot]->uninit();
8319 unconst(mNetworkAdapters[slot]).setNull();
8320 }
8321 }
8322
8323 if (mUSBDeviceFilters)
8324 {
8325 mUSBDeviceFilters->uninit();
8326 unconst(mUSBDeviceFilters).setNull();
8327 }
8328
8329 if (mAudioAdapter)
8330 {
8331 mAudioAdapter->uninit();
8332 unconst(mAudioAdapter).setNull();
8333 }
8334
8335 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8336 {
8337 if (mParallelPorts[slot])
8338 {
8339 mParallelPorts[slot]->uninit();
8340 unconst(mParallelPorts[slot]).setNull();
8341 }
8342 }
8343
8344 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8345 {
8346 if (mSerialPorts[slot])
8347 {
8348 mSerialPorts[slot]->uninit();
8349 unconst(mSerialPorts[slot]).setNull();
8350 }
8351 }
8352
8353 if (mVRDEServer)
8354 {
8355 mVRDEServer->uninit();
8356 unconst(mVRDEServer).setNull();
8357 }
8358
8359 if (mBIOSSettings)
8360 {
8361 mBIOSSettings->uninit();
8362 unconst(mBIOSSettings).setNull();
8363 }
8364
8365 /* Deassociate media (only when a real Machine or a SnapshotMachine
8366 * instance is uninitialized; SessionMachine instances refer to real
8367 * Machine media). This is necessary for a clean re-initialization of
8368 * the VM after successfully re-checking the accessibility state. Note
8369 * that in case of normal Machine or SnapshotMachine uninitialization (as
8370 * a result of unregistering or deleting the snapshot), outdated media
8371 * attachments will already be uninitialized and deleted, so this
8372 * code will not affect them. */
8373 if ( !!mMediaData
8374 && (!i_isSessionMachine())
8375 )
8376 {
8377 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8378 it != mMediaData->mAttachments.end();
8379 ++it)
8380 {
8381 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8382 if (pMedium.isNull())
8383 continue;
8384 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8385 AssertComRC(rc);
8386 }
8387 }
8388
8389 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8390 {
8391 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8392 if (mData->mFirstSnapshot)
8393 {
8394 // snapshots tree is protected by machine write lock; strictly
8395 // this isn't necessary here since we're deleting the entire
8396 // machine, but otherwise we assert in Snapshot::uninit()
8397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8398 mData->mFirstSnapshot->uninit();
8399 mData->mFirstSnapshot.setNull();
8400 }
8401
8402 mData->mCurrentSnapshot.setNull();
8403 }
8404
8405 /* free data structures (the essential mData structure is not freed here
8406 * since it may be still in use) */
8407 mMediaData.free();
8408 mStorageControllers.free();
8409 mUSBControllers.free();
8410 mHWData.free();
8411 mUserData.free();
8412 mSSData.free();
8413}
8414
8415/**
8416 * Returns a pointer to the Machine object for this machine that acts like a
8417 * parent for complex machine data objects such as shared folders, etc.
8418 *
8419 * For primary Machine objects and for SnapshotMachine objects, returns this
8420 * object's pointer itself. For SessionMachine objects, returns the peer
8421 * (primary) machine pointer.
8422 */
8423Machine* Machine::i_getMachine()
8424{
8425 if (i_isSessionMachine())
8426 return (Machine*)mPeer;
8427 return this;
8428}
8429
8430/**
8431 * Makes sure that there are no machine state dependents. If necessary, waits
8432 * for the number of dependents to drop to zero.
8433 *
8434 * Make sure this method is called from under this object's write lock to
8435 * guarantee that no new dependents may be added when this method returns
8436 * control to the caller.
8437 *
8438 * @note Locks this object for writing. The lock will be released while waiting
8439 * (if necessary).
8440 *
8441 * @warning To be used only in methods that change the machine state!
8442 */
8443void Machine::i_ensureNoStateDependencies()
8444{
8445 AssertReturnVoid(isWriteLockOnCurrentThread());
8446
8447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8448
8449 /* Wait for all state dependents if necessary */
8450 if (mData->mMachineStateDeps != 0)
8451 {
8452 /* lazy semaphore creation */
8453 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8454 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8455
8456 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8457 mData->mMachineStateDeps));
8458
8459 ++mData->mMachineStateChangePending;
8460
8461 /* reset the semaphore before waiting, the last dependent will signal
8462 * it */
8463 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8464
8465 alock.release();
8466
8467 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8468
8469 alock.acquire();
8470
8471 -- mData->mMachineStateChangePending;
8472 }
8473}
8474
8475/**
8476 * Changes the machine state and informs callbacks.
8477 *
8478 * This method is not intended to fail so it either returns S_OK or asserts (and
8479 * returns a failure).
8480 *
8481 * @note Locks this object for writing.
8482 */
8483HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8484{
8485 LogFlowThisFuncEnter();
8486 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8487 Assert(aMachineState != MachineState_Null);
8488
8489 AutoCaller autoCaller(this);
8490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8491
8492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8493
8494 /* wait for state dependents to drop to zero */
8495 i_ensureNoStateDependencies();
8496
8497 MachineState_T const enmOldState = mData->mMachineState;
8498 if (enmOldState != aMachineState)
8499 {
8500 mData->mMachineState = aMachineState;
8501 RTTimeNow(&mData->mLastStateChange);
8502
8503#ifdef VBOX_WITH_DTRACE_R3_MAIN
8504 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8505#endif
8506 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8507 }
8508
8509 LogFlowThisFuncLeave();
8510 return S_OK;
8511}
8512
8513/**
8514 * Searches for a shared folder with the given logical name
8515 * in the collection of shared folders.
8516 *
8517 * @param aName logical name of the shared folder
8518 * @param aSharedFolder where to return the found object
8519 * @param aSetError whether to set the error info if the folder is
8520 * not found
8521 * @return
8522 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8523 *
8524 * @note
8525 * must be called from under the object's lock!
8526 */
8527HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8528 ComObjPtr<SharedFolder> &aSharedFolder,
8529 bool aSetError /* = false */)
8530{
8531 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8532 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8533 it != mHWData->mSharedFolders.end();
8534 ++it)
8535 {
8536 SharedFolder *pSF = *it;
8537 AutoCaller autoCaller(pSF);
8538 if (pSF->i_getName() == aName)
8539 {
8540 aSharedFolder = pSF;
8541 rc = S_OK;
8542 break;
8543 }
8544 }
8545
8546 if (aSetError && FAILED(rc))
8547 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8548
8549 return rc;
8550}
8551
8552/**
8553 * Initializes all machine instance data from the given settings structures
8554 * from XML. The exception is the machine UUID which needs special handling
8555 * depending on the caller's use case, so the caller needs to set that herself.
8556 *
8557 * This gets called in several contexts during machine initialization:
8558 *
8559 * -- When machine XML exists on disk already and needs to be loaded into memory,
8560 * for example, from registeredInit() to load all registered machines on
8561 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8562 * attached to the machine should be part of some media registry already.
8563 *
8564 * -- During OVF import, when a machine config has been constructed from an
8565 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8566 * ensure that the media listed as attachments in the config (which have
8567 * been imported from the OVF) receive the correct registry ID.
8568 *
8569 * -- During VM cloning.
8570 *
8571 * @param config Machine settings from XML.
8572 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8573 * for each attached medium in the config.
8574 * @return
8575 */
8576HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8577 const Guid *puuidRegistry)
8578{
8579 // copy name, description, OS type, teleporter, UTC etc.
8580 mUserData->s = config.machineUserData;
8581
8582 // Decode the Icon overide data from config userdata and set onto Machine.
8583 #define DECODE_STR_MAX _1M
8584 const char* pszStr = config.machineUserData.ovIcon.c_str();
8585 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8586 if (cbOut > DECODE_STR_MAX)
8587 return setError(E_FAIL,
8588 tr("Icon Data too long.'%d' > '%d'"),
8589 cbOut,
8590 DECODE_STR_MAX);
8591 mUserData->mIcon.resize(cbOut);
8592 int vrc = VINF_SUCCESS;
8593 if (cbOut)
8594 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8595 if (RT_FAILURE(vrc))
8596 {
8597 mUserData->mIcon.resize(0);
8598 return setError(E_FAIL,
8599 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8600 pszStr,
8601 vrc);
8602 }
8603
8604 // look up the object by Id to check it is valid
8605 ComPtr<IGuestOSType> guestOSType;
8606 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8607 guestOSType.asOutParam());
8608 if (FAILED(rc)) return rc;
8609
8610 // stateFile (optional)
8611 if (config.strStateFile.isEmpty())
8612 mSSData->strStateFilePath.setNull();
8613 else
8614 {
8615 Utf8Str stateFilePathFull(config.strStateFile);
8616 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8617 if (RT_FAILURE(vrc))
8618 return setError(E_FAIL,
8619 tr("Invalid saved state file path '%s' (%Rrc)"),
8620 config.strStateFile.c_str(),
8621 vrc);
8622 mSSData->strStateFilePath = stateFilePathFull;
8623 }
8624
8625 // snapshot folder needs special processing so set it again
8626 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8627 if (FAILED(rc)) return rc;
8628
8629 /* Copy the extra data items (Not in any case config is already the same as
8630 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8631 * make sure the extra data map is copied). */
8632 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8633
8634 /* currentStateModified (optional, default is true) */
8635 mData->mCurrentStateModified = config.fCurrentStateModified;
8636
8637 mData->mLastStateChange = config.timeLastStateChange;
8638
8639 /*
8640 * note: all mUserData members must be assigned prior this point because
8641 * we need to commit changes in order to let mUserData be shared by all
8642 * snapshot machine instances.
8643 */
8644 mUserData.commitCopy();
8645
8646 // machine registry, if present (must be loaded before snapshots)
8647 if (config.canHaveOwnMediaRegistry())
8648 {
8649 // determine machine folder
8650 Utf8Str strMachineFolder = i_getSettingsFileFull();
8651 strMachineFolder.stripFilename();
8652 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8653 config.mediaRegistry,
8654 strMachineFolder);
8655 if (FAILED(rc)) return rc;
8656 }
8657
8658 /* Snapshot node (optional) */
8659 size_t cRootSnapshots;
8660 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8661 {
8662 // there must be only one root snapshot
8663 Assert(cRootSnapshots == 1);
8664
8665 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8666
8667 rc = i_loadSnapshot(snap,
8668 config.uuidCurrentSnapshot,
8669 NULL); // no parent == first snapshot
8670 if (FAILED(rc)) return rc;
8671 }
8672
8673 // hardware data
8674 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8675 if (FAILED(rc)) return rc;
8676
8677 // load storage controllers
8678 rc = i_loadStorageControllers(config.storageMachine,
8679 puuidRegistry,
8680 NULL /* puuidSnapshot */);
8681 if (FAILED(rc)) return rc;
8682
8683 /*
8684 * NOTE: the assignment below must be the last thing to do,
8685 * otherwise it will be not possible to change the settings
8686 * somewhere in the code above because all setters will be
8687 * blocked by i_checkStateDependency(MutableStateDep).
8688 */
8689
8690 /* set the machine state to Aborted or Saved when appropriate */
8691 if (config.fAborted)
8692 {
8693 mSSData->strStateFilePath.setNull();
8694
8695 /* no need to use i_setMachineState() during init() */
8696 mData->mMachineState = MachineState_Aborted;
8697 }
8698 else if (!mSSData->strStateFilePath.isEmpty())
8699 {
8700 /* no need to use i_setMachineState() during init() */
8701 mData->mMachineState = MachineState_Saved;
8702 }
8703
8704 // after loading settings, we are no longer different from the XML on disk
8705 mData->flModifications = 0;
8706
8707 return S_OK;
8708}
8709
8710/**
8711 * Recursively loads all snapshots starting from the given.
8712 *
8713 * @param aNode <Snapshot> node.
8714 * @param aCurSnapshotId Current snapshot ID from the settings file.
8715 * @param aParentSnapshot Parent snapshot.
8716 */
8717HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8718 const Guid &aCurSnapshotId,
8719 Snapshot *aParentSnapshot)
8720{
8721 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8722 AssertReturn(!i_isSessionMachine(), E_FAIL);
8723
8724 HRESULT rc = S_OK;
8725
8726 Utf8Str strStateFile;
8727 if (!data.strStateFile.isEmpty())
8728 {
8729 /* optional */
8730 strStateFile = data.strStateFile;
8731 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8732 if (RT_FAILURE(vrc))
8733 return setError(E_FAIL,
8734 tr("Invalid saved state file path '%s' (%Rrc)"),
8735 strStateFile.c_str(),
8736 vrc);
8737 }
8738
8739 /* create a snapshot machine object */
8740 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8741 pSnapshotMachine.createObject();
8742 rc = pSnapshotMachine->initFromSettings(this,
8743 data.hardware,
8744 &data.debugging,
8745 &data.autostart,
8746 data.storage,
8747 data.uuid.ref(),
8748 strStateFile);
8749 if (FAILED(rc)) return rc;
8750
8751 /* create a snapshot object */
8752 ComObjPtr<Snapshot> pSnapshot;
8753 pSnapshot.createObject();
8754 /* initialize the snapshot */
8755 rc = pSnapshot->init(mParent, // VirtualBox object
8756 data.uuid,
8757 data.strName,
8758 data.strDescription,
8759 data.timestamp,
8760 pSnapshotMachine,
8761 aParentSnapshot);
8762 if (FAILED(rc)) return rc;
8763
8764 /* memorize the first snapshot if necessary */
8765 if (!mData->mFirstSnapshot)
8766 mData->mFirstSnapshot = pSnapshot;
8767
8768 /* memorize the current snapshot when appropriate */
8769 if ( !mData->mCurrentSnapshot
8770 && pSnapshot->i_getId() == aCurSnapshotId
8771 )
8772 mData->mCurrentSnapshot = pSnapshot;
8773
8774 // now create the children
8775 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8776 it != data.llChildSnapshots.end();
8777 ++it)
8778 {
8779 const settings::Snapshot &childData = *it;
8780 // recurse
8781 rc = i_loadSnapshot(childData,
8782 aCurSnapshotId,
8783 pSnapshot); // parent = the one we created above
8784 if (FAILED(rc)) return rc;
8785 }
8786
8787 return rc;
8788}
8789
8790/**
8791 * Loads settings into mHWData.
8792 *
8793 * @param data Reference to the hardware settings.
8794 * @param pDbg Pointer to the debugging settings.
8795 * @param pAutostart Pointer to the autostart settings.
8796 */
8797HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8798 const settings::Autostart *pAutostart)
8799{
8800 AssertReturn(!i_isSessionMachine(), E_FAIL);
8801
8802 HRESULT rc = S_OK;
8803
8804 try
8805 {
8806 /* The hardware version attribute (optional). */
8807 mHWData->mHWVersion = data.strVersion;
8808 mHWData->mHardwareUUID = data.uuid;
8809
8810 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8811 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8812 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8813 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8814 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8815 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8816 mHWData->mPAEEnabled = data.fPAE;
8817 mHWData->mLongMode = data.enmLongMode;
8818 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8819 mHWData->mCPUCount = data.cCPUs;
8820 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8821 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8822 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8823
8824 // cpu
8825 if (mHWData->mCPUHotPlugEnabled)
8826 {
8827 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8828 it != data.llCpus.end();
8829 ++it)
8830 {
8831 const settings::Cpu &cpu = *it;
8832
8833 mHWData->mCPUAttached[cpu.ulId] = true;
8834 }
8835 }
8836
8837 // cpuid leafs
8838 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8839 it != data.llCpuIdLeafs.end();
8840 ++it)
8841 {
8842 const settings::CpuIdLeaf &leaf = *it;
8843
8844 switch (leaf.ulId)
8845 {
8846 case 0x0:
8847 case 0x1:
8848 case 0x2:
8849 case 0x3:
8850 case 0x4:
8851 case 0x5:
8852 case 0x6:
8853 case 0x7:
8854 case 0x8:
8855 case 0x9:
8856 case 0xA:
8857 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8858 break;
8859
8860 case 0x80000000:
8861 case 0x80000001:
8862 case 0x80000002:
8863 case 0x80000003:
8864 case 0x80000004:
8865 case 0x80000005:
8866 case 0x80000006:
8867 case 0x80000007:
8868 case 0x80000008:
8869 case 0x80000009:
8870 case 0x8000000A:
8871 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8872 break;
8873
8874 default:
8875 /* just ignore */
8876 break;
8877 }
8878 }
8879
8880 mHWData->mMemorySize = data.ulMemorySizeMB;
8881 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8882
8883 // boot order
8884 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8885 {
8886 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8887 if (it == data.mapBootOrder.end())
8888 mHWData->mBootOrder[i] = DeviceType_Null;
8889 else
8890 mHWData->mBootOrder[i] = it->second;
8891 }
8892
8893 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8894 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8895 mHWData->mMonitorCount = data.cMonitors;
8896 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8897 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8898 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8899 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8900 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8901 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8902 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8903 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8904 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8905 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8906 if (!data.strVideoCaptureFile.isEmpty())
8907 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8908 else
8909 mHWData->mVideoCaptureFile.setNull();
8910 mHWData->mFirmwareType = data.firmwareType;
8911 mHWData->mPointingHIDType = data.pointingHIDType;
8912 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8913 mHWData->mChipsetType = data.chipsetType;
8914 mHWData->mParavirtProvider = data.paravirtProvider;
8915 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8916 mHWData->mHPETEnabled = data.fHPETEnabled;
8917
8918 /* VRDEServer */
8919 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8920 if (FAILED(rc)) return rc;
8921
8922 /* BIOS */
8923 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8924 if (FAILED(rc)) return rc;
8925
8926 // Bandwidth control (must come before network adapters)
8927 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8928 if (FAILED(rc)) return rc;
8929
8930 /* Shared folders */
8931 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8932 it != data.usbSettings.llUSBControllers.end();
8933 ++it)
8934 {
8935 const settings::USBController &settingsCtrl = *it;
8936 ComObjPtr<USBController> newCtrl;
8937
8938 newCtrl.createObject();
8939 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8940 mUSBControllers->push_back(newCtrl);
8941 }
8942
8943 /* USB device filters */
8944 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8945 if (FAILED(rc)) return rc;
8946
8947 // network adapters
8948 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8949 size_t oldCount = mNetworkAdapters.size();
8950 if (newCount > oldCount)
8951 {
8952 mNetworkAdapters.resize(newCount);
8953 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8954 {
8955 unconst(mNetworkAdapters[slot]).createObject();
8956 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8957 }
8958 }
8959 else if (newCount < oldCount)
8960 mNetworkAdapters.resize(newCount);
8961 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8962 it != data.llNetworkAdapters.end();
8963 ++it)
8964 {
8965 const settings::NetworkAdapter &nic = *it;
8966
8967 /* slot unicity is guaranteed by XML Schema */
8968 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8969 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8970 if (FAILED(rc)) return rc;
8971 }
8972
8973 // serial ports
8974 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8975 it != data.llSerialPorts.end();
8976 ++it)
8977 {
8978 const settings::SerialPort &s = *it;
8979
8980 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8981 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8982 if (FAILED(rc)) return rc;
8983 }
8984
8985 // parallel ports (optional)
8986 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8987 it != data.llParallelPorts.end();
8988 ++it)
8989 {
8990 const settings::ParallelPort &p = *it;
8991
8992 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8993 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8994 if (FAILED(rc)) return rc;
8995 }
8996
8997 /* AudioAdapter */
8998 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8999 if (FAILED(rc)) return rc;
9000
9001 /* Shared folders */
9002 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9003 it != data.llSharedFolders.end();
9004 ++it)
9005 {
9006 const settings::SharedFolder &sf = *it;
9007
9008 ComObjPtr<SharedFolder> sharedFolder;
9009 /* Check for double entries. Not allowed! */
9010 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9011 if (SUCCEEDED(rc))
9012 return setError(VBOX_E_OBJECT_IN_USE,
9013 tr("Shared folder named '%s' already exists"),
9014 sf.strName.c_str());
9015
9016 /* Create the new shared folder. Don't break on error. This will be
9017 * reported when the machine starts. */
9018 sharedFolder.createObject();
9019 rc = sharedFolder->init(i_getMachine(),
9020 sf.strName,
9021 sf.strHostPath,
9022 RT_BOOL(sf.fWritable),
9023 RT_BOOL(sf.fAutoMount),
9024 false /* fFailOnError */);
9025 if (FAILED(rc)) return rc;
9026 mHWData->mSharedFolders.push_back(sharedFolder);
9027 }
9028
9029 // Clipboard
9030 mHWData->mClipboardMode = data.clipboardMode;
9031
9032 // drag'n'drop
9033 mHWData->mDnDMode = data.dndMode;
9034
9035 // guest settings
9036 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9037
9038 // IO settings
9039 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9040 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9041
9042 // Host PCI devices
9043 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9044 it != data.pciAttachments.end();
9045 ++it)
9046 {
9047 const settings::HostPCIDeviceAttachment &hpda = *it;
9048 ComObjPtr<PCIDeviceAttachment> pda;
9049
9050 pda.createObject();
9051 pda->i_loadSettings(this, hpda);
9052 mHWData->mPCIDeviceAssignments.push_back(pda);
9053 }
9054
9055 /*
9056 * (The following isn't really real hardware, but it lives in HWData
9057 * for reasons of convenience.)
9058 */
9059
9060#ifdef VBOX_WITH_GUEST_PROPS
9061 /* Guest properties (optional) */
9062
9063 /* Only load transient guest properties for configs which have saved
9064 * state, because there shouldn't be any for powered off VMs. The same
9065 * logic applies for snapshots, as offline snapshots shouldn't have
9066 * any such properties. They confuse the code in various places.
9067 * Note: can't rely on the machine state, as it isn't set yet. */
9068 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9069 /* apologies for the hacky unconst() usage, but this needs hacking
9070 * actually inconsistent settings into consistency, otherwise there
9071 * will be some corner cases where the inconsistency survives
9072 * surprisingly long without getting fixed, especially for snapshots
9073 * as there are no config changes. */
9074 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9075 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9076 it != llGuestProperties.end();
9077 /*nothing*/)
9078 {
9079 const settings::GuestProperty &prop = *it;
9080 uint32_t fFlags = guestProp::NILFLAG;
9081 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9082 if ( fSkipTransientGuestProperties
9083 && ( fFlags & guestProp::TRANSIENT
9084 || fFlags & guestProp::TRANSRESET))
9085 {
9086 it = llGuestProperties.erase(it);
9087 continue;
9088 }
9089 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9090 mHWData->mGuestProperties[prop.strName] = property;
9091 ++it;
9092 }
9093#endif /* VBOX_WITH_GUEST_PROPS defined */
9094
9095 rc = i_loadDebugging(pDbg);
9096 if (FAILED(rc))
9097 return rc;
9098
9099 mHWData->mAutostart = *pAutostart;
9100
9101 /* default frontend */
9102 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9103 }
9104 catch(std::bad_alloc &)
9105 {
9106 return E_OUTOFMEMORY;
9107 }
9108
9109 AssertComRC(rc);
9110 return rc;
9111}
9112
9113/**
9114 * Called from Machine::loadHardware() to load the debugging settings of the
9115 * machine.
9116 *
9117 * @param pDbg Pointer to the settings.
9118 */
9119HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9120{
9121 mHWData->mDebugging = *pDbg;
9122 /* no more processing currently required, this will probably change. */
9123 return S_OK;
9124}
9125
9126/**
9127 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9128 *
9129 * @param data
9130 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9131 * @param puuidSnapshot
9132 * @return
9133 */
9134HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9135 const Guid *puuidRegistry,
9136 const Guid *puuidSnapshot)
9137{
9138 AssertReturn(!i_isSessionMachine(), E_FAIL);
9139
9140 HRESULT rc = S_OK;
9141
9142 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9143 it != data.llStorageControllers.end();
9144 ++it)
9145 {
9146 const settings::StorageController &ctlData = *it;
9147
9148 ComObjPtr<StorageController> pCtl;
9149 /* Try to find one with the name first. */
9150 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9151 if (SUCCEEDED(rc))
9152 return setError(VBOX_E_OBJECT_IN_USE,
9153 tr("Storage controller named '%s' already exists"),
9154 ctlData.strName.c_str());
9155
9156 pCtl.createObject();
9157 rc = pCtl->init(this,
9158 ctlData.strName,
9159 ctlData.storageBus,
9160 ctlData.ulInstance,
9161 ctlData.fBootable);
9162 if (FAILED(rc)) return rc;
9163
9164 mStorageControllers->push_back(pCtl);
9165
9166 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9167 if (FAILED(rc)) return rc;
9168
9169 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9170 if (FAILED(rc)) return rc;
9171
9172 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9173 if (FAILED(rc)) return rc;
9174
9175 /* Load the attached devices now. */
9176 rc = i_loadStorageDevices(pCtl,
9177 ctlData,
9178 puuidRegistry,
9179 puuidSnapshot);
9180 if (FAILED(rc)) return rc;
9181 }
9182
9183 return S_OK;
9184}
9185
9186/**
9187 * Called from i_loadStorageControllers for a controller's devices.
9188 *
9189 * @param aStorageController
9190 * @param data
9191 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9192 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9193 * @return
9194 */
9195HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9196 const settings::StorageController &data,
9197 const Guid *puuidRegistry,
9198 const Guid *puuidSnapshot)
9199{
9200 HRESULT rc = S_OK;
9201
9202 /* paranoia: detect duplicate attachments */
9203 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9204 it != data.llAttachedDevices.end();
9205 ++it)
9206 {
9207 const settings::AttachedDevice &ad = *it;
9208
9209 for (settings::AttachedDevicesList::const_iterator it2 = it;
9210 it2 != data.llAttachedDevices.end();
9211 ++it2)
9212 {
9213 if (it == it2)
9214 continue;
9215
9216 const settings::AttachedDevice &ad2 = *it2;
9217
9218 if ( ad.lPort == ad2.lPort
9219 && ad.lDevice == ad2.lDevice)
9220 {
9221 return setError(E_FAIL,
9222 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9223 aStorageController->i_getName().c_str(),
9224 ad.lPort,
9225 ad.lDevice,
9226 mUserData->s.strName.c_str());
9227 }
9228 }
9229 }
9230
9231 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9232 it != data.llAttachedDevices.end();
9233 ++it)
9234 {
9235 const settings::AttachedDevice &dev = *it;
9236 ComObjPtr<Medium> medium;
9237
9238 switch (dev.deviceType)
9239 {
9240 case DeviceType_Floppy:
9241 case DeviceType_DVD:
9242 if (dev.strHostDriveSrc.isNotEmpty())
9243 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9244 false /* fRefresh */, medium);
9245 else
9246 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9247 dev.uuid,
9248 false /* fRefresh */,
9249 false /* aSetError */,
9250 medium);
9251 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9252 // This is not an error. The host drive or UUID might have vanished, so just go
9253 // ahead without this removeable medium attachment
9254 rc = S_OK;
9255 break;
9256
9257 case DeviceType_HardDisk:
9258 {
9259 /* find a hard disk by UUID */
9260 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9261 if (FAILED(rc))
9262 {
9263 if (i_isSnapshotMachine())
9264 {
9265 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9266 // so the user knows that the bad disk is in a snapshot somewhere
9267 com::ErrorInfo info;
9268 return setError(E_FAIL,
9269 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9270 puuidSnapshot->raw(),
9271 info.getText().raw());
9272 }
9273 else
9274 return rc;
9275 }
9276
9277 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9278
9279 if (medium->i_getType() == MediumType_Immutable)
9280 {
9281 if (i_isSnapshotMachine())
9282 return setError(E_FAIL,
9283 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9284 "of the virtual machine '%s' ('%s')"),
9285 medium->i_getLocationFull().c_str(),
9286 dev.uuid.raw(),
9287 puuidSnapshot->raw(),
9288 mUserData->s.strName.c_str(),
9289 mData->m_strConfigFileFull.c_str());
9290
9291 return setError(E_FAIL,
9292 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9293 medium->i_getLocationFull().c_str(),
9294 dev.uuid.raw(),
9295 mUserData->s.strName.c_str(),
9296 mData->m_strConfigFileFull.c_str());
9297 }
9298
9299 if (medium->i_getType() == MediumType_MultiAttach)
9300 {
9301 if (i_isSnapshotMachine())
9302 return setError(E_FAIL,
9303 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9304 "of the virtual machine '%s' ('%s')"),
9305 medium->i_getLocationFull().c_str(),
9306 dev.uuid.raw(),
9307 puuidSnapshot->raw(),
9308 mUserData->s.strName.c_str(),
9309 mData->m_strConfigFileFull.c_str());
9310
9311 return setError(E_FAIL,
9312 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9313 medium->i_getLocationFull().c_str(),
9314 dev.uuid.raw(),
9315 mUserData->s.strName.c_str(),
9316 mData->m_strConfigFileFull.c_str());
9317 }
9318
9319 if ( !i_isSnapshotMachine()
9320 && medium->i_getChildren().size() != 0
9321 )
9322 return setError(E_FAIL,
9323 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9324 "because it has %d differencing child hard disks"),
9325 medium->i_getLocationFull().c_str(),
9326 dev.uuid.raw(),
9327 mUserData->s.strName.c_str(),
9328 mData->m_strConfigFileFull.c_str(),
9329 medium->i_getChildren().size());
9330
9331 if (i_findAttachment(mMediaData->mAttachments,
9332 medium))
9333 return setError(E_FAIL,
9334 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9335 medium->i_getLocationFull().c_str(),
9336 dev.uuid.raw(),
9337 mUserData->s.strName.c_str(),
9338 mData->m_strConfigFileFull.c_str());
9339
9340 break;
9341 }
9342
9343 default:
9344 return setError(E_FAIL,
9345 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9346 medium->i_getLocationFull().c_str(),
9347 mUserData->s.strName.c_str(),
9348 mData->m_strConfigFileFull.c_str());
9349 }
9350
9351 if (FAILED(rc))
9352 break;
9353
9354 /* Bandwidth groups are loaded at this point. */
9355 ComObjPtr<BandwidthGroup> pBwGroup;
9356
9357 if (!dev.strBwGroup.isEmpty())
9358 {
9359 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9360 if (FAILED(rc))
9361 return setError(E_FAIL,
9362 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9363 medium->i_getLocationFull().c_str(),
9364 dev.strBwGroup.c_str(),
9365 mUserData->s.strName.c_str(),
9366 mData->m_strConfigFileFull.c_str());
9367 pBwGroup->i_reference();
9368 }
9369
9370 const Bstr controllerName = aStorageController->i_getName();
9371 ComObjPtr<MediumAttachment> pAttachment;
9372 pAttachment.createObject();
9373 rc = pAttachment->init(this,
9374 medium,
9375 controllerName,
9376 dev.lPort,
9377 dev.lDevice,
9378 dev.deviceType,
9379 false,
9380 dev.fPassThrough,
9381 dev.fTempEject,
9382 dev.fNonRotational,
9383 dev.fDiscard,
9384 dev.fHotPluggable,
9385 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9386 if (FAILED(rc)) break;
9387
9388 /* associate the medium with this machine and snapshot */
9389 if (!medium.isNull())
9390 {
9391 AutoCaller medCaller(medium);
9392 if (FAILED(medCaller.rc())) return medCaller.rc();
9393 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9394
9395 if (i_isSnapshotMachine())
9396 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9397 else
9398 rc = medium->i_addBackReference(mData->mUuid);
9399 /* If the medium->addBackReference fails it sets an appropriate
9400 * error message, so no need to do any guesswork here. */
9401
9402 if (puuidRegistry)
9403 // caller wants registry ID to be set on all attached media (OVF import case)
9404 medium->i_addRegistry(*puuidRegistry);
9405 }
9406
9407 if (FAILED(rc))
9408 break;
9409
9410 /* back up mMediaData to let registeredInit() properly rollback on failure
9411 * (= limited accessibility) */
9412 i_setModified(IsModified_Storage);
9413 mMediaData.backup();
9414 mMediaData->mAttachments.push_back(pAttachment);
9415 }
9416
9417 return rc;
9418}
9419
9420/**
9421 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9422 *
9423 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9424 * @param aSnapshot where to return the found snapshot
9425 * @param aSetError true to set extended error info on failure
9426 */
9427HRESULT Machine::i_findSnapshotById(const Guid &aId,
9428 ComObjPtr<Snapshot> &aSnapshot,
9429 bool aSetError /* = false */)
9430{
9431 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9432
9433 if (!mData->mFirstSnapshot)
9434 {
9435 if (aSetError)
9436 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9437 return E_FAIL;
9438 }
9439
9440 if (aId.isZero())
9441 aSnapshot = mData->mFirstSnapshot;
9442 else
9443 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9444
9445 if (!aSnapshot)
9446 {
9447 if (aSetError)
9448 return setError(E_FAIL,
9449 tr("Could not find a snapshot with UUID {%s}"),
9450 aId.toString().c_str());
9451 return E_FAIL;
9452 }
9453
9454 return S_OK;
9455}
9456
9457/**
9458 * Returns the snapshot with the given name or fails of no such snapshot.
9459 *
9460 * @param aName snapshot name to find
9461 * @param aSnapshot where to return the found snapshot
9462 * @param aSetError true to set extended error info on failure
9463 */
9464HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9465 ComObjPtr<Snapshot> &aSnapshot,
9466 bool aSetError /* = false */)
9467{
9468 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9469
9470 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9471
9472 if (!mData->mFirstSnapshot)
9473 {
9474 if (aSetError)
9475 return setError(VBOX_E_OBJECT_NOT_FOUND,
9476 tr("This machine does not have any snapshots"));
9477 return VBOX_E_OBJECT_NOT_FOUND;
9478 }
9479
9480 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9481
9482 if (!aSnapshot)
9483 {
9484 if (aSetError)
9485 return setError(VBOX_E_OBJECT_NOT_FOUND,
9486 tr("Could not find a snapshot named '%s'"), strName.c_str());
9487 return VBOX_E_OBJECT_NOT_FOUND;
9488 }
9489
9490 return S_OK;
9491}
9492
9493/**
9494 * Returns a storage controller object with the given name.
9495 *
9496 * @param aName storage controller name to find
9497 * @param aStorageController where to return the found storage controller
9498 * @param aSetError true to set extended error info on failure
9499 */
9500HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9501 ComObjPtr<StorageController> &aStorageController,
9502 bool aSetError /* = false */)
9503{
9504 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9505
9506 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9507 it != mStorageControllers->end();
9508 ++it)
9509 {
9510 if ((*it)->i_getName() == aName)
9511 {
9512 aStorageController = (*it);
9513 return S_OK;
9514 }
9515 }
9516
9517 if (aSetError)
9518 return setError(VBOX_E_OBJECT_NOT_FOUND,
9519 tr("Could not find a storage controller named '%s'"),
9520 aName.c_str());
9521 return VBOX_E_OBJECT_NOT_FOUND;
9522}
9523
9524/**
9525 * Returns a USB controller object with the given name.
9526 *
9527 * @param aName USB controller name to find
9528 * @param aUSBController where to return the found USB controller
9529 * @param aSetError true to set extended error info on failure
9530 */
9531HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9532 ComObjPtr<USBController> &aUSBController,
9533 bool aSetError /* = false */)
9534{
9535 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9536
9537 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9538 it != mUSBControllers->end();
9539 ++it)
9540 {
9541 if ((*it)->i_getName() == aName)
9542 {
9543 aUSBController = (*it);
9544 return S_OK;
9545 }
9546 }
9547
9548 if (aSetError)
9549 return setError(VBOX_E_OBJECT_NOT_FOUND,
9550 tr("Could not find a storage controller named '%s'"),
9551 aName.c_str());
9552 return VBOX_E_OBJECT_NOT_FOUND;
9553}
9554
9555/**
9556 * Returns the number of USB controller instance of the given type.
9557 *
9558 * @param enmType USB controller type.
9559 */
9560ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9561{
9562 ULONG cCtrls = 0;
9563
9564 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9565 it != mUSBControllers->end();
9566 ++it)
9567 {
9568 if ((*it)->i_getControllerType() == enmType)
9569 cCtrls++;
9570 }
9571
9572 return cCtrls;
9573}
9574
9575HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9576 MediaData::AttachmentList &atts)
9577{
9578 AutoCaller autoCaller(this);
9579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9580
9581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9582
9583 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9584 it != mMediaData->mAttachments.end();
9585 ++it)
9586 {
9587 const ComObjPtr<MediumAttachment> &pAtt = *it;
9588 // should never happen, but deal with NULL pointers in the list.
9589 AssertStmt(!pAtt.isNull(), continue);
9590
9591 // getControllerName() needs caller+read lock
9592 AutoCaller autoAttCaller(pAtt);
9593 if (FAILED(autoAttCaller.rc()))
9594 {
9595 atts.clear();
9596 return autoAttCaller.rc();
9597 }
9598 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9599
9600 if (pAtt->i_getControllerName() == aName)
9601 atts.push_back(pAtt);
9602 }
9603
9604 return S_OK;
9605}
9606
9607
9608/**
9609 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9610 * file if the machine name was changed and about creating a new settings file
9611 * if this is a new machine.
9612 *
9613 * @note Must be never called directly but only from #saveSettings().
9614 */
9615HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9616{
9617 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9618
9619 HRESULT rc = S_OK;
9620
9621 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9622
9623 /// @todo need to handle primary group change, too
9624
9625 /* attempt to rename the settings file if machine name is changed */
9626 if ( mUserData->s.fNameSync
9627 && mUserData.isBackedUp()
9628 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9629 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9630 )
9631 {
9632 bool dirRenamed = false;
9633 bool fileRenamed = false;
9634
9635 Utf8Str configFile, newConfigFile;
9636 Utf8Str configFilePrev, newConfigFilePrev;
9637 Utf8Str configDir, newConfigDir;
9638
9639 do
9640 {
9641 int vrc = VINF_SUCCESS;
9642
9643 Utf8Str name = mUserData.backedUpData()->s.strName;
9644 Utf8Str newName = mUserData->s.strName;
9645 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9646 if (group == "/")
9647 group.setNull();
9648 Utf8Str newGroup = mUserData->s.llGroups.front();
9649 if (newGroup == "/")
9650 newGroup.setNull();
9651
9652 configFile = mData->m_strConfigFileFull;
9653
9654 /* first, rename the directory if it matches the group and machine name */
9655 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9656 group.c_str(), RTPATH_DELIMITER, name.c_str());
9657 /** @todo hack, make somehow use of ComposeMachineFilename */
9658 if (mUserData->s.fDirectoryIncludesUUID)
9659 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9660 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9661 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9662 /** @todo hack, make somehow use of ComposeMachineFilename */
9663 if (mUserData->s.fDirectoryIncludesUUID)
9664 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9665 configDir = configFile;
9666 configDir.stripFilename();
9667 newConfigDir = configDir;
9668 if ( configDir.length() >= groupPlusName.length()
9669 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9670 groupPlusName.c_str()))
9671 {
9672 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9673 Utf8Str newConfigBaseDir(newConfigDir);
9674 newConfigDir.append(newGroupPlusName);
9675 /* consistency: use \ if appropriate on the platform */
9676 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9677 /* new dir and old dir cannot be equal here because of 'if'
9678 * above and because name != newName */
9679 Assert(configDir != newConfigDir);
9680 if (!fSettingsFileIsNew)
9681 {
9682 /* perform real rename only if the machine is not new */
9683 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9684 if ( vrc == VERR_FILE_NOT_FOUND
9685 || vrc == VERR_PATH_NOT_FOUND)
9686 {
9687 /* create the parent directory, then retry renaming */
9688 Utf8Str parent(newConfigDir);
9689 parent.stripFilename();
9690 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9691 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9692 }
9693 if (RT_FAILURE(vrc))
9694 {
9695 rc = setError(E_FAIL,
9696 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9697 configDir.c_str(),
9698 newConfigDir.c_str(),
9699 vrc);
9700 break;
9701 }
9702 /* delete subdirectories which are no longer needed */
9703 Utf8Str dir(configDir);
9704 dir.stripFilename();
9705 while (dir != newConfigBaseDir && dir != ".")
9706 {
9707 vrc = RTDirRemove(dir.c_str());
9708 if (RT_FAILURE(vrc))
9709 break;
9710 dir.stripFilename();
9711 }
9712 dirRenamed = true;
9713 }
9714 }
9715
9716 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9717 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9718
9719 /* then try to rename the settings file itself */
9720 if (newConfigFile != configFile)
9721 {
9722 /* get the path to old settings file in renamed directory */
9723 configFile = Utf8StrFmt("%s%c%s",
9724 newConfigDir.c_str(),
9725 RTPATH_DELIMITER,
9726 RTPathFilename(configFile.c_str()));
9727 if (!fSettingsFileIsNew)
9728 {
9729 /* perform real rename only if the machine is not new */
9730 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9731 if (RT_FAILURE(vrc))
9732 {
9733 rc = setError(E_FAIL,
9734 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9735 configFile.c_str(),
9736 newConfigFile.c_str(),
9737 vrc);
9738 break;
9739 }
9740 fileRenamed = true;
9741 configFilePrev = configFile;
9742 configFilePrev += "-prev";
9743 newConfigFilePrev = newConfigFile;
9744 newConfigFilePrev += "-prev";
9745 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9746 }
9747 }
9748
9749 // update m_strConfigFileFull amd mConfigFile
9750 mData->m_strConfigFileFull = newConfigFile;
9751 // compute the relative path too
9752 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9753
9754 // store the old and new so that VirtualBox::i_saveSettings() can update
9755 // the media registry
9756 if ( mData->mRegistered
9757 && (configDir != newConfigDir || configFile != newConfigFile))
9758 {
9759 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9760
9761 if (pfNeedsGlobalSaveSettings)
9762 *pfNeedsGlobalSaveSettings = true;
9763 }
9764
9765 // in the saved state file path, replace the old directory with the new directory
9766 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9767 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9768
9769 // and do the same thing for the saved state file paths of all the online snapshots
9770 if (mData->mFirstSnapshot)
9771 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9772 newConfigDir.c_str());
9773 }
9774 while (0);
9775
9776 if (FAILED(rc))
9777 {
9778 /* silently try to rename everything back */
9779 if (fileRenamed)
9780 {
9781 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9782 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9783 }
9784 if (dirRenamed)
9785 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9786 }
9787
9788 if (FAILED(rc)) return rc;
9789 }
9790
9791 if (fSettingsFileIsNew)
9792 {
9793 /* create a virgin config file */
9794 int vrc = VINF_SUCCESS;
9795
9796 /* ensure the settings directory exists */
9797 Utf8Str path(mData->m_strConfigFileFull);
9798 path.stripFilename();
9799 if (!RTDirExists(path.c_str()))
9800 {
9801 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9802 if (RT_FAILURE(vrc))
9803 {
9804 return setError(E_FAIL,
9805 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9806 path.c_str(),
9807 vrc);
9808 }
9809 }
9810
9811 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9812 path = Utf8Str(mData->m_strConfigFileFull);
9813 RTFILE f = NIL_RTFILE;
9814 vrc = RTFileOpen(&f, path.c_str(),
9815 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9816 if (RT_FAILURE(vrc))
9817 return setError(E_FAIL,
9818 tr("Could not create the settings file '%s' (%Rrc)"),
9819 path.c_str(),
9820 vrc);
9821 RTFileClose(f);
9822 }
9823
9824 return rc;
9825}
9826
9827/**
9828 * Saves and commits machine data, user data and hardware data.
9829 *
9830 * Note that on failure, the data remains uncommitted.
9831 *
9832 * @a aFlags may combine the following flags:
9833 *
9834 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9835 * Used when saving settings after an operation that makes them 100%
9836 * correspond to the settings from the current snapshot.
9837 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9838 * #isReallyModified() returns false. This is necessary for cases when we
9839 * change machine data directly, not through the backup()/commit() mechanism.
9840 * - SaveS_Force: settings will be saved without doing a deep compare of the
9841 * settings structures. This is used when this is called because snapshots
9842 * have changed to avoid the overhead of the deep compare.
9843 *
9844 * @note Must be called from under this object's write lock. Locks children for
9845 * writing.
9846 *
9847 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9848 * initialized to false and that will be set to true by this function if
9849 * the caller must invoke VirtualBox::i_saveSettings() because the global
9850 * settings have changed. This will happen if a machine rename has been
9851 * saved and the global machine and media registries will therefore need
9852 * updating.
9853 */
9854HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9855 int aFlags /*= 0*/)
9856{
9857 LogFlowThisFuncEnter();
9858
9859 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9860
9861 /* make sure child objects are unable to modify the settings while we are
9862 * saving them */
9863 i_ensureNoStateDependencies();
9864
9865 AssertReturn(!i_isSnapshotMachine(),
9866 E_FAIL);
9867
9868 HRESULT rc = S_OK;
9869 bool fNeedsWrite = false;
9870
9871 /* First, prepare to save settings. It will care about renaming the
9872 * settings directory and file if the machine name was changed and about
9873 * creating a new settings file if this is a new machine. */
9874 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9875 if (FAILED(rc)) return rc;
9876
9877 // keep a pointer to the current settings structures
9878 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9879 settings::MachineConfigFile *pNewConfig = NULL;
9880
9881 try
9882 {
9883 // make a fresh one to have everyone write stuff into
9884 pNewConfig = new settings::MachineConfigFile(NULL);
9885 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9886
9887 // now go and copy all the settings data from COM to the settings structures
9888 // (this calles i_saveSettings() on all the COM objects in the machine)
9889 i_copyMachineDataToSettings(*pNewConfig);
9890
9891 if (aFlags & SaveS_ResetCurStateModified)
9892 {
9893 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9894 mData->mCurrentStateModified = FALSE;
9895 fNeedsWrite = true; // always, no need to compare
9896 }
9897 else if (aFlags & SaveS_Force)
9898 {
9899 fNeedsWrite = true; // always, no need to compare
9900 }
9901 else
9902 {
9903 if (!mData->mCurrentStateModified)
9904 {
9905 // do a deep compare of the settings that we just saved with the settings
9906 // previously stored in the config file; this invokes MachineConfigFile::operator==
9907 // which does a deep compare of all the settings, which is expensive but less expensive
9908 // than writing out XML in vain
9909 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9910
9911 // could still be modified if any settings changed
9912 mData->mCurrentStateModified = fAnySettingsChanged;
9913
9914 fNeedsWrite = fAnySettingsChanged;
9915 }
9916 else
9917 fNeedsWrite = true;
9918 }
9919
9920 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9921
9922 if (fNeedsWrite)
9923 // now spit it all out!
9924 pNewConfig->write(mData->m_strConfigFileFull);
9925
9926 mData->pMachineConfigFile = pNewConfig;
9927 delete pOldConfig;
9928 i_commit();
9929
9930 // after saving settings, we are no longer different from the XML on disk
9931 mData->flModifications = 0;
9932 }
9933 catch (HRESULT err)
9934 {
9935 // we assume that error info is set by the thrower
9936 rc = err;
9937
9938 // restore old config
9939 delete pNewConfig;
9940 mData->pMachineConfigFile = pOldConfig;
9941 }
9942 catch (...)
9943 {
9944 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9945 }
9946
9947 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9948 {
9949 /* Fire the data change event, even on failure (since we've already
9950 * committed all data). This is done only for SessionMachines because
9951 * mutable Machine instances are always not registered (i.e. private
9952 * to the client process that creates them) and thus don't need to
9953 * inform callbacks. */
9954 if (i_isSessionMachine())
9955 mParent->i_onMachineDataChange(mData->mUuid);
9956 }
9957
9958 LogFlowThisFunc(("rc=%08X\n", rc));
9959 LogFlowThisFuncLeave();
9960 return rc;
9961}
9962
9963/**
9964 * Implementation for saving the machine settings into the given
9965 * settings::MachineConfigFile instance. This copies machine extradata
9966 * from the previous machine config file in the instance data, if any.
9967 *
9968 * This gets called from two locations:
9969 *
9970 * -- Machine::i_saveSettings(), during the regular XML writing;
9971 *
9972 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9973 * exported to OVF and we write the VirtualBox proprietary XML
9974 * into a <vbox:Machine> tag.
9975 *
9976 * This routine fills all the fields in there, including snapshots, *except*
9977 * for the following:
9978 *
9979 * -- fCurrentStateModified. There is some special logic associated with that.
9980 *
9981 * The caller can then call MachineConfigFile::write() or do something else
9982 * with it.
9983 *
9984 * Caller must hold the machine lock!
9985 *
9986 * This throws XML errors and HRESULT, so the caller must have a catch block!
9987 */
9988void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9989{
9990 // deep copy extradata
9991 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9992
9993 config.uuid = mData->mUuid;
9994
9995 // copy name, description, OS type, teleport, UTC etc.
9996 config.machineUserData = mUserData->s;
9997
9998 // Encode the Icon Override data from Machine and store on config userdata.
9999 std::vector<BYTE> iconByte;
10000 getIcon(iconByte);
10001 ssize_t cbData = iconByte.size();
10002 if (cbData > 0)
10003 {
10004 ssize_t cchOut = RTBase64EncodedLength(cbData);
10005 Utf8Str strIconData;
10006 strIconData.reserve(cchOut+1);
10007 int vrc = RTBase64Encode(&iconByte.front(), cbData,
10008 strIconData.mutableRaw(), strIconData.capacity(),
10009 NULL);
10010 if (RT_FAILURE(vrc))
10011 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10012 strIconData.jolt();
10013 config.machineUserData.ovIcon = strIconData;
10014 }
10015 else
10016 config.machineUserData.ovIcon.setNull();
10017
10018 if ( mData->mMachineState == MachineState_Saved
10019 || mData->mMachineState == MachineState_Restoring
10020 // when doing certain snapshot operations we may or may not have
10021 // a saved state in the current state, so keep everything as is
10022 || ( ( mData->mMachineState == MachineState_Snapshotting
10023 || mData->mMachineState == MachineState_DeletingSnapshot
10024 || mData->mMachineState == MachineState_RestoringSnapshot)
10025 && (!mSSData->strStateFilePath.isEmpty())
10026 )
10027 )
10028 {
10029 Assert(!mSSData->strStateFilePath.isEmpty());
10030 /* try to make the file name relative to the settings file dir */
10031 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10032 }
10033 else
10034 {
10035 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10036 config.strStateFile.setNull();
10037 }
10038
10039 if (mData->mCurrentSnapshot)
10040 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10041 else
10042 config.uuidCurrentSnapshot.clear();
10043
10044 config.timeLastStateChange = mData->mLastStateChange;
10045 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10046 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10047
10048 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10049 if (FAILED(rc)) throw rc;
10050
10051 rc = i_saveStorageControllers(config.storageMachine);
10052 if (FAILED(rc)) throw rc;
10053
10054 // save machine's media registry if this is VirtualBox 4.0 or later
10055 if (config.canHaveOwnMediaRegistry())
10056 {
10057 // determine machine folder
10058 Utf8Str strMachineFolder = i_getSettingsFileFull();
10059 strMachineFolder.stripFilename();
10060 mParent->i_saveMediaRegistry(config.mediaRegistry,
10061 i_getId(), // only media with registry ID == machine UUID
10062 strMachineFolder);
10063 // this throws HRESULT
10064 }
10065
10066 // save snapshots
10067 rc = i_saveAllSnapshots(config);
10068 if (FAILED(rc)) throw rc;
10069}
10070
10071/**
10072 * Saves all snapshots of the machine into the given machine config file. Called
10073 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10074 * @param config
10075 * @return
10076 */
10077HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10078{
10079 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10080
10081 HRESULT rc = S_OK;
10082
10083 try
10084 {
10085 config.llFirstSnapshot.clear();
10086
10087 if (mData->mFirstSnapshot)
10088 {
10089 // the settings use a list for "the first snapshot"
10090 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10091
10092 // get reference to the snapshot on the list and work on that
10093 // element straight in the list to avoid excessive copying later
10094 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10095 if (FAILED(rc)) throw rc;
10096 }
10097
10098// if (mType == IsSessionMachine)
10099// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10100
10101 }
10102 catch (HRESULT err)
10103 {
10104 /* we assume that error info is set by the thrower */
10105 rc = err;
10106 }
10107 catch (...)
10108 {
10109 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10110 }
10111
10112 return rc;
10113}
10114
10115/**
10116 * Saves the VM hardware configuration. It is assumed that the
10117 * given node is empty.
10118 *
10119 * @param data Reference to the settings object for the hardware config.
10120 * @param pDbg Pointer to the settings object for the debugging config
10121 * which happens to live in mHWData.
10122 * @param pAutostart Pointer to the settings object for the autostart config
10123 * which happens to live in mHWData.
10124 */
10125HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10126 settings::Autostart *pAutostart)
10127{
10128 HRESULT rc = S_OK;
10129
10130 try
10131 {
10132 /* The hardware version attribute (optional).
10133 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10134 if ( mHWData->mHWVersion == "1"
10135 && mSSData->strStateFilePath.isEmpty()
10136 )
10137 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10138 other point needs to be found where this can be done. */
10139
10140 data.strVersion = mHWData->mHWVersion;
10141 data.uuid = mHWData->mHardwareUUID;
10142
10143 // CPU
10144 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10145 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10146 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10147 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10148 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10149 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10150 data.fPAE = !!mHWData->mPAEEnabled;
10151 data.enmLongMode = mHWData->mLongMode;
10152 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10153 data.cCPUs = mHWData->mCPUCount;
10154 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10155 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10156 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10157
10158 data.llCpus.clear();
10159 if (data.fCpuHotPlug)
10160 {
10161 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10162 {
10163 if (mHWData->mCPUAttached[idx])
10164 {
10165 settings::Cpu cpu;
10166 cpu.ulId = idx;
10167 data.llCpus.push_back(cpu);
10168 }
10169 }
10170 }
10171
10172 /* Standard and Extended CPUID leafs. */
10173 data.llCpuIdLeafs.clear();
10174 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10175 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10176 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10177 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10178 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10179 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10180
10181 // memory
10182 data.ulMemorySizeMB = mHWData->mMemorySize;
10183 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10184
10185 // firmware
10186 data.firmwareType = mHWData->mFirmwareType;
10187
10188 // HID
10189 data.pointingHIDType = mHWData->mPointingHIDType;
10190 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10191
10192 // chipset
10193 data.chipsetType = mHWData->mChipsetType;
10194
10195 // paravirt
10196 data.paravirtProvider = mHWData->mParavirtProvider;
10197
10198
10199 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10200
10201 // HPET
10202 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10203
10204 // boot order
10205 data.mapBootOrder.clear();
10206 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10207 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10208
10209 // display
10210 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10211 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10212 data.cMonitors = mHWData->mMonitorCount;
10213 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10214 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10215 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10216 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10217 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10218 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10219 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10220 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10221 {
10222 if (mHWData->maVideoCaptureScreens[i])
10223 ASMBitSet(&data.u64VideoCaptureScreens, i);
10224 else
10225 ASMBitClear(&data.u64VideoCaptureScreens, i);
10226 }
10227 /* store relative video capture file if possible */
10228 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10229
10230 /* VRDEServer settings (optional) */
10231 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10232 if (FAILED(rc)) throw rc;
10233
10234 /* BIOS (required) */
10235 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10236 if (FAILED(rc)) throw rc;
10237
10238 /* USB Controller (required) */
10239 data.usbSettings.llUSBControllers.clear();
10240 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10241 {
10242 ComObjPtr<USBController> ctrl = *it;
10243 settings::USBController settingsCtrl;
10244
10245 settingsCtrl.strName = ctrl->i_getName();
10246 settingsCtrl.enmType = ctrl->i_getControllerType();
10247
10248 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10249 }
10250
10251 /* USB device filters (required) */
10252 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10253 if (FAILED(rc)) throw rc;
10254
10255 /* Network adapters (required) */
10256 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10257 data.llNetworkAdapters.clear();
10258 /* Write out only the nominal number of network adapters for this
10259 * chipset type. Since Machine::commit() hasn't been called there
10260 * may be extra NIC settings in the vector. */
10261 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10262 {
10263 settings::NetworkAdapter nic;
10264 nic.ulSlot = (uint32_t)slot;
10265 /* paranoia check... must not be NULL, but must not crash either. */
10266 if (mNetworkAdapters[slot])
10267 {
10268 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10269 if (FAILED(rc)) throw rc;
10270
10271 data.llNetworkAdapters.push_back(nic);
10272 }
10273 }
10274
10275 /* Serial ports */
10276 data.llSerialPorts.clear();
10277 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10278 {
10279 settings::SerialPort s;
10280 s.ulSlot = slot;
10281 rc = mSerialPorts[slot]->i_saveSettings(s);
10282 if (FAILED(rc)) return rc;
10283
10284 data.llSerialPorts.push_back(s);
10285 }
10286
10287 /* Parallel ports */
10288 data.llParallelPorts.clear();
10289 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10290 {
10291 settings::ParallelPort p;
10292 p.ulSlot = slot;
10293 rc = mParallelPorts[slot]->i_saveSettings(p);
10294 if (FAILED(rc)) return rc;
10295
10296 data.llParallelPorts.push_back(p);
10297 }
10298
10299 /* Audio adapter */
10300 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10301 if (FAILED(rc)) return rc;
10302
10303 /* Shared folders */
10304 data.llSharedFolders.clear();
10305 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10306 it != mHWData->mSharedFolders.end();
10307 ++it)
10308 {
10309 SharedFolder *pSF = *it;
10310 AutoCaller sfCaller(pSF);
10311 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10312 settings::SharedFolder sf;
10313 sf.strName = pSF->i_getName();
10314 sf.strHostPath = pSF->i_getHostPath();
10315 sf.fWritable = !!pSF->i_isWritable();
10316 sf.fAutoMount = !!pSF->i_isAutoMounted();
10317
10318 data.llSharedFolders.push_back(sf);
10319 }
10320
10321 // clipboard
10322 data.clipboardMode = mHWData->mClipboardMode;
10323
10324 // drag'n'drop
10325 data.dndMode = mHWData->mDnDMode;
10326
10327 /* Guest */
10328 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10329
10330 // IO settings
10331 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10332 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10333
10334 /* BandwidthControl (required) */
10335 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10336 if (FAILED(rc)) throw rc;
10337
10338 /* Host PCI devices */
10339 data.pciAttachments.clear();
10340 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10341 it != mHWData->mPCIDeviceAssignments.end();
10342 ++it)
10343 {
10344 ComObjPtr<PCIDeviceAttachment> pda = *it;
10345 settings::HostPCIDeviceAttachment hpda;
10346
10347 rc = pda->i_saveSettings(hpda);
10348 if (FAILED(rc)) throw rc;
10349
10350 data.pciAttachments.push_back(hpda);
10351 }
10352
10353
10354 // guest properties
10355 data.llGuestProperties.clear();
10356#ifdef VBOX_WITH_GUEST_PROPS
10357 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10358 it != mHWData->mGuestProperties.end();
10359 ++it)
10360 {
10361 HWData::GuestProperty property = it->second;
10362
10363 /* Remove transient guest properties at shutdown unless we
10364 * are saving state. Note that restoring snapshot intentionally
10365 * keeps them, they will be removed if appropriate once the final
10366 * machine state is set (as crashes etc. need to work). */
10367 if ( ( mData->mMachineState == MachineState_PoweredOff
10368 || mData->mMachineState == MachineState_Aborted
10369 || mData->mMachineState == MachineState_Teleported)
10370 && ( property.mFlags & guestProp::TRANSIENT
10371 || property.mFlags & guestProp::TRANSRESET))
10372 continue;
10373 settings::GuestProperty prop;
10374 prop.strName = it->first;
10375 prop.strValue = property.strValue;
10376 prop.timestamp = property.mTimestamp;
10377 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10378 guestProp::writeFlags(property.mFlags, szFlags);
10379 prop.strFlags = szFlags;
10380
10381 data.llGuestProperties.push_back(prop);
10382 }
10383
10384 /* I presume this doesn't require a backup(). */
10385 mData->mGuestPropertiesModified = FALSE;
10386#endif /* VBOX_WITH_GUEST_PROPS defined */
10387
10388 *pDbg = mHWData->mDebugging;
10389 *pAutostart = mHWData->mAutostart;
10390
10391 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10392 }
10393 catch(std::bad_alloc &)
10394 {
10395 return E_OUTOFMEMORY;
10396 }
10397
10398 AssertComRC(rc);
10399 return rc;
10400}
10401
10402/**
10403 * Saves the storage controller configuration.
10404 *
10405 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10406 */
10407HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10408{
10409 data.llStorageControllers.clear();
10410
10411 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10412 it != mStorageControllers->end();
10413 ++it)
10414 {
10415 HRESULT rc;
10416 ComObjPtr<StorageController> pCtl = *it;
10417
10418 settings::StorageController ctl;
10419 ctl.strName = pCtl->i_getName();
10420 ctl.controllerType = pCtl->i_getControllerType();
10421 ctl.storageBus = pCtl->i_getStorageBus();
10422 ctl.ulInstance = pCtl->i_getInstance();
10423 ctl.fBootable = pCtl->i_getBootable();
10424
10425 /* Save the port count. */
10426 ULONG portCount;
10427 rc = pCtl->COMGETTER(PortCount)(&portCount);
10428 ComAssertComRCRet(rc, rc);
10429 ctl.ulPortCount = portCount;
10430
10431 /* Save fUseHostIOCache */
10432 BOOL fUseHostIOCache;
10433 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10434 ComAssertComRCRet(rc, rc);
10435 ctl.fUseHostIOCache = !!fUseHostIOCache;
10436
10437 /* save the devices now. */
10438 rc = i_saveStorageDevices(pCtl, ctl);
10439 ComAssertComRCRet(rc, rc);
10440
10441 data.llStorageControllers.push_back(ctl);
10442 }
10443
10444 return S_OK;
10445}
10446
10447/**
10448 * Saves the hard disk configuration.
10449 */
10450HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10451 settings::StorageController &data)
10452{
10453 MediaData::AttachmentList atts;
10454
10455 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10456 if (FAILED(rc)) return rc;
10457
10458 data.llAttachedDevices.clear();
10459 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10460 it != atts.end();
10461 ++it)
10462 {
10463 settings::AttachedDevice dev;
10464 IMediumAttachment *iA = *it;
10465 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10466 Medium *pMedium = pAttach->i_getMedium();
10467
10468 dev.deviceType = pAttach->i_getType();
10469 dev.lPort = pAttach->i_getPort();
10470 dev.lDevice = pAttach->i_getDevice();
10471 dev.fPassThrough = pAttach->i_getPassthrough();
10472 dev.fHotPluggable = pAttach->i_getHotPluggable();
10473 if (pMedium)
10474 {
10475 if (pMedium->i_isHostDrive())
10476 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10477 else
10478 dev.uuid = pMedium->i_getId();
10479 dev.fTempEject = pAttach->i_getTempEject();
10480 dev.fNonRotational = pAttach->i_getNonRotational();
10481 dev.fDiscard = pAttach->i_getDiscard();
10482 }
10483
10484 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10485
10486 data.llAttachedDevices.push_back(dev);
10487 }
10488
10489 return S_OK;
10490}
10491
10492/**
10493 * Saves machine state settings as defined by aFlags
10494 * (SaveSTS_* values).
10495 *
10496 * @param aFlags Combination of SaveSTS_* flags.
10497 *
10498 * @note Locks objects for writing.
10499 */
10500HRESULT Machine::i_saveStateSettings(int aFlags)
10501{
10502 if (aFlags == 0)
10503 return S_OK;
10504
10505 AutoCaller autoCaller(this);
10506 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10507
10508 /* This object's write lock is also necessary to serialize file access
10509 * (prevent concurrent reads and writes) */
10510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10511
10512 HRESULT rc = S_OK;
10513
10514 Assert(mData->pMachineConfigFile);
10515
10516 try
10517 {
10518 if (aFlags & SaveSTS_CurStateModified)
10519 mData->pMachineConfigFile->fCurrentStateModified = true;
10520
10521 if (aFlags & SaveSTS_StateFilePath)
10522 {
10523 if (!mSSData->strStateFilePath.isEmpty())
10524 /* try to make the file name relative to the settings file dir */
10525 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10526 else
10527 mData->pMachineConfigFile->strStateFile.setNull();
10528 }
10529
10530 if (aFlags & SaveSTS_StateTimeStamp)
10531 {
10532 Assert( mData->mMachineState != MachineState_Aborted
10533 || mSSData->strStateFilePath.isEmpty());
10534
10535 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10536
10537 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10538//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10539 }
10540
10541 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10542 }
10543 catch (...)
10544 {
10545 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10546 }
10547
10548 return rc;
10549}
10550
10551/**
10552 * Ensures that the given medium is added to a media registry. If this machine
10553 * was created with 4.0 or later, then the machine registry is used. Otherwise
10554 * the global VirtualBox media registry is used.
10555 *
10556 * Caller must NOT hold machine lock, media tree or any medium locks!
10557 *
10558 * @param pMedium
10559 */
10560void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10561{
10562 /* Paranoia checks: do not hold machine or media tree locks. */
10563 AssertReturnVoid(!isWriteLockOnCurrentThread());
10564 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10565
10566 ComObjPtr<Medium> pBase;
10567 {
10568 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10569 pBase = pMedium->i_getBase();
10570 }
10571
10572 /* Paranoia checks: do not hold medium locks. */
10573 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10574 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10575
10576 // decide which medium registry to use now that the medium is attached:
10577 Guid uuid;
10578 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10579 // machine XML is VirtualBox 4.0 or higher:
10580 uuid = i_getId(); // machine UUID
10581 else
10582 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10583
10584 if (pMedium->i_addRegistry(uuid))
10585 mParent->i_markRegistryModified(uuid);
10586
10587 /* For more complex hard disk structures it can happen that the base
10588 * medium isn't yet associated with any medium registry. Do that now. */
10589 if (pMedium != pBase)
10590 {
10591 /* Tree lock needed by Medium::addRegistry when recursing. */
10592 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10593 if (pBase->i_addRegistryRecursive(uuid))
10594 {
10595 treeLock.release();
10596 mParent->i_markRegistryModified(uuid);
10597 }
10598 }
10599}
10600
10601/**
10602 * Creates differencing hard disks for all normal hard disks attached to this
10603 * machine and a new set of attachments to refer to created disks.
10604 *
10605 * Used when taking a snapshot or when deleting the current state. Gets called
10606 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10607 *
10608 * This method assumes that mMediaData contains the original hard disk attachments
10609 * it needs to create diffs for. On success, these attachments will be replaced
10610 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10611 * called to delete created diffs which will also rollback mMediaData and restore
10612 * whatever was backed up before calling this method.
10613 *
10614 * Attachments with non-normal hard disks are left as is.
10615 *
10616 * If @a aOnline is @c false then the original hard disks that require implicit
10617 * diffs will be locked for reading. Otherwise it is assumed that they are
10618 * already locked for writing (when the VM was started). Note that in the latter
10619 * case it is responsibility of the caller to lock the newly created diffs for
10620 * writing if this method succeeds.
10621 *
10622 * @param aProgress Progress object to run (must contain at least as
10623 * many operations left as the number of hard disks
10624 * attached).
10625 * @param aOnline Whether the VM was online prior to this operation.
10626 *
10627 * @note The progress object is not marked as completed, neither on success nor
10628 * on failure. This is a responsibility of the caller.
10629 *
10630 * @note Locks this object and the media tree for writing.
10631 */
10632HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10633 ULONG aWeight,
10634 bool aOnline)
10635{
10636 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10637
10638 AutoCaller autoCaller(this);
10639 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10640
10641 AutoMultiWriteLock2 alock(this->lockHandle(),
10642 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10643
10644 /* must be in a protective state because we release the lock below */
10645 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10646 || mData->mMachineState == MachineState_OnlineSnapshotting
10647 || mData->mMachineState == MachineState_LiveSnapshotting
10648 || mData->mMachineState == MachineState_RestoringSnapshot
10649 || mData->mMachineState == MachineState_DeletingSnapshot
10650 , E_FAIL);
10651
10652 HRESULT rc = S_OK;
10653
10654 // use appropriate locked media map (online or offline)
10655 MediumLockListMap lockedMediaOffline;
10656 MediumLockListMap *lockedMediaMap;
10657 if (aOnline)
10658 lockedMediaMap = &mData->mSession.mLockedMedia;
10659 else
10660 lockedMediaMap = &lockedMediaOffline;
10661
10662 try
10663 {
10664 if (!aOnline)
10665 {
10666 /* lock all attached hard disks early to detect "in use"
10667 * situations before creating actual diffs */
10668 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10669 it != mMediaData->mAttachments.end();
10670 ++it)
10671 {
10672 MediumAttachment* pAtt = *it;
10673 if (pAtt->i_getType() == DeviceType_HardDisk)
10674 {
10675 Medium* pMedium = pAtt->i_getMedium();
10676 Assert(pMedium);
10677
10678 MediumLockList *pMediumLockList(new MediumLockList());
10679 alock.release();
10680 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10681 false /* fMediumLockWrite */,
10682 false /* fMediumLockWriteAll */,
10683 NULL,
10684 *pMediumLockList);
10685 alock.acquire();
10686 if (FAILED(rc))
10687 {
10688 delete pMediumLockList;
10689 throw rc;
10690 }
10691 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10692 if (FAILED(rc))
10693 {
10694 throw setError(rc,
10695 tr("Collecting locking information for all attached media failed"));
10696 }
10697 }
10698 }
10699
10700 /* Now lock all media. If this fails, nothing is locked. */
10701 alock.release();
10702 rc = lockedMediaMap->Lock();
10703 alock.acquire();
10704 if (FAILED(rc))
10705 {
10706 throw setError(rc,
10707 tr("Locking of attached media failed"));
10708 }
10709 }
10710
10711 /* remember the current list (note that we don't use backup() since
10712 * mMediaData may be already backed up) */
10713 MediaData::AttachmentList atts = mMediaData->mAttachments;
10714
10715 /* start from scratch */
10716 mMediaData->mAttachments.clear();
10717
10718 /* go through remembered attachments and create diffs for normal hard
10719 * disks and attach them */
10720 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10721 it != atts.end();
10722 ++it)
10723 {
10724 MediumAttachment* pAtt = *it;
10725
10726 DeviceType_T devType = pAtt->i_getType();
10727 Medium* pMedium = pAtt->i_getMedium();
10728
10729 if ( devType != DeviceType_HardDisk
10730 || pMedium == NULL
10731 || pMedium->i_getType() != MediumType_Normal)
10732 {
10733 /* copy the attachment as is */
10734
10735 /** @todo the progress object created in SessionMachine::TakeSnaphot
10736 * only expects operations for hard disks. Later other
10737 * device types need to show up in the progress as well. */
10738 if (devType == DeviceType_HardDisk)
10739 {
10740 if (pMedium == NULL)
10741 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10742 aWeight); // weight
10743 else
10744 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10745 pMedium->i_getBase()->i_getName().c_str()).raw(),
10746 aWeight); // weight
10747 }
10748
10749 mMediaData->mAttachments.push_back(pAtt);
10750 continue;
10751 }
10752
10753 /* need a diff */
10754 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10755 pMedium->i_getBase()->i_getName().c_str()).raw(),
10756 aWeight); // weight
10757
10758 Utf8Str strFullSnapshotFolder;
10759 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10760
10761 ComObjPtr<Medium> diff;
10762 diff.createObject();
10763 // store the diff in the same registry as the parent
10764 // (this cannot fail here because we can't create implicit diffs for
10765 // unregistered images)
10766 Guid uuidRegistryParent;
10767 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10768 Assert(fInRegistry); NOREF(fInRegistry);
10769 rc = diff->init(mParent,
10770 pMedium->i_getPreferredDiffFormat(),
10771 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10772 uuidRegistryParent,
10773 DeviceType_HardDisk);
10774 if (FAILED(rc)) throw rc;
10775
10776 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10777 * the push_back? Looks like we're going to release medium with the
10778 * wrong kind of lock (general issue with if we fail anywhere at all)
10779 * and an orphaned VDI in the snapshots folder. */
10780
10781 /* update the appropriate lock list */
10782 MediumLockList *pMediumLockList;
10783 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10784 AssertComRCThrowRC(rc);
10785 if (aOnline)
10786 {
10787 alock.release();
10788 /* The currently attached medium will be read-only, change
10789 * the lock type to read. */
10790 rc = pMediumLockList->Update(pMedium, false);
10791 alock.acquire();
10792 AssertComRCThrowRC(rc);
10793 }
10794
10795 /* release the locks before the potentially lengthy operation */
10796 alock.release();
10797 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10798 pMediumLockList,
10799 NULL /* aProgress */,
10800 true /* aWait */);
10801 alock.acquire();
10802 if (FAILED(rc)) throw rc;
10803
10804 /* actual lock list update is done in Medium::commitMedia */
10805
10806 rc = diff->i_addBackReference(mData->mUuid);
10807 AssertComRCThrowRC(rc);
10808
10809 /* add a new attachment */
10810 ComObjPtr<MediumAttachment> attachment;
10811 attachment.createObject();
10812 rc = attachment->init(this,
10813 diff,
10814 pAtt->i_getControllerName(),
10815 pAtt->i_getPort(),
10816 pAtt->i_getDevice(),
10817 DeviceType_HardDisk,
10818 true /* aImplicit */,
10819 false /* aPassthrough */,
10820 false /* aTempEject */,
10821 pAtt->i_getNonRotational(),
10822 pAtt->i_getDiscard(),
10823 pAtt->i_getHotPluggable(),
10824 pAtt->i_getBandwidthGroup());
10825 if (FAILED(rc)) throw rc;
10826
10827 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10828 AssertComRCThrowRC(rc);
10829 mMediaData->mAttachments.push_back(attachment);
10830 }
10831 }
10832 catch (HRESULT aRC) { rc = aRC; }
10833
10834 /* unlock all hard disks we locked when there is no VM */
10835 if (!aOnline)
10836 {
10837 ErrorInfoKeeper eik;
10838
10839 HRESULT rc1 = lockedMediaMap->Clear();
10840 AssertComRC(rc1);
10841 }
10842
10843 return rc;
10844}
10845
10846/**
10847 * Deletes implicit differencing hard disks created either by
10848 * #i_createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10849 *
10850 * Note that to delete hard disks created by #AttachDevice() this method is
10851 * called from #fixupMedia() when the changes are rolled back.
10852 *
10853 * @note Locks this object and the media tree for writing.
10854 */
10855HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10856{
10857 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10858
10859 AutoCaller autoCaller(this);
10860 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10861
10862 AutoMultiWriteLock2 alock(this->lockHandle(),
10863 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10864
10865 /* We absolutely must have backed up state. */
10866 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10867
10868 /* Check if there are any implicitly created diff images. */
10869 bool fImplicitDiffs = false;
10870 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10871 it != mMediaData->mAttachments.end();
10872 ++it)
10873 {
10874 const ComObjPtr<MediumAttachment> &pAtt = *it;
10875 if (pAtt->i_isImplicit())
10876 {
10877 fImplicitDiffs = true;
10878 break;
10879 }
10880 }
10881 /* If there is nothing to do, leave early. This saves lots of image locking
10882 * effort. It also avoids a MachineStateChanged event without real reason.
10883 * This is important e.g. when loading a VM config, because there should be
10884 * no events. Otherwise API clients can become thoroughly confused for
10885 * inaccessible VMs (the code for loading VM configs uses this method for
10886 * cleanup if the config makes no sense), as they take such events as an
10887 * indication that the VM is alive, and they would force the VM config to
10888 * be reread, leading to an endless loop. */
10889 if (!fImplicitDiffs)
10890 return S_OK;
10891
10892 HRESULT rc = S_OK;
10893 MachineState_T oldState = mData->mMachineState;
10894
10895 /* will release the lock before the potentially lengthy operation,
10896 * so protect with the special state (unless already protected) */
10897 if ( oldState != MachineState_Snapshotting
10898 && oldState != MachineState_OnlineSnapshotting
10899 && oldState != MachineState_LiveSnapshotting
10900 && oldState != MachineState_RestoringSnapshot
10901 && oldState != MachineState_DeletingSnapshot
10902 && oldState != MachineState_DeletingSnapshotOnline
10903 && oldState != MachineState_DeletingSnapshotPaused
10904 )
10905 i_setMachineState(MachineState_SettingUp);
10906
10907 // use appropriate locked media map (online or offline)
10908 MediumLockListMap lockedMediaOffline;
10909 MediumLockListMap *lockedMediaMap;
10910 if (aOnline)
10911 lockedMediaMap = &mData->mSession.mLockedMedia;
10912 else
10913 lockedMediaMap = &lockedMediaOffline;
10914
10915 try
10916 {
10917 if (!aOnline)
10918 {
10919 /* lock all attached hard disks early to detect "in use"
10920 * situations before deleting actual diffs */
10921 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10922 it != mMediaData->mAttachments.end();
10923 ++it)
10924 {
10925 MediumAttachment* pAtt = *it;
10926 if (pAtt->i_getType() == DeviceType_HardDisk)
10927 {
10928 Medium* pMedium = pAtt->i_getMedium();
10929 Assert(pMedium);
10930
10931 MediumLockList *pMediumLockList(new MediumLockList());
10932 alock.release();
10933 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10934 false /* fMediumLockWrite */,
10935 false /* fMediumLockWriteAll */,
10936 NULL,
10937 *pMediumLockList);
10938 alock.acquire();
10939
10940 if (FAILED(rc))
10941 {
10942 delete pMediumLockList;
10943 throw rc;
10944 }
10945
10946 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10947 if (FAILED(rc))
10948 throw rc;
10949 }
10950 }
10951
10952 if (FAILED(rc))
10953 throw rc;
10954 } // end of offline
10955
10956 /* Lock lists are now up to date and include implicitly created media */
10957
10958 /* Go through remembered attachments and delete all implicitly created
10959 * diffs and fix up the attachment information */
10960 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10961 MediaData::AttachmentList implicitAtts;
10962 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10963 it != mMediaData->mAttachments.end();
10964 ++it)
10965 {
10966 ComObjPtr<MediumAttachment> pAtt = *it;
10967 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10968 if (pMedium.isNull())
10969 continue;
10970
10971 // Implicit attachments go on the list for deletion and back references are removed.
10972 if (pAtt->i_isImplicit())
10973 {
10974 /* Deassociate and mark for deletion */
10975 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10976 rc = pMedium->i_removeBackReference(mData->mUuid);
10977 if (FAILED(rc))
10978 throw rc;
10979 implicitAtts.push_back(pAtt);
10980 continue;
10981 }
10982
10983 /* Was this medium attached before? */
10984 if (!i_findAttachment(oldAtts, pMedium))
10985 {
10986 /* no: de-associate */
10987 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10988 rc = pMedium->i_removeBackReference(mData->mUuid);
10989 if (FAILED(rc))
10990 throw rc;
10991 continue;
10992 }
10993 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10994 }
10995
10996 /* If there are implicit attachments to delete, throw away the lock
10997 * map contents (which will unlock all media) since the medium
10998 * attachments will be rolled back. Below we need to completely
10999 * recreate the lock map anyway since it is infinitely complex to
11000 * do this incrementally (would need reconstructing each attachment
11001 * change, which would be extremely hairy). */
11002 if (implicitAtts.size() != 0)
11003 {
11004 ErrorInfoKeeper eik;
11005
11006 HRESULT rc1 = lockedMediaMap->Clear();
11007 AssertComRC(rc1);
11008 }
11009
11010 /* rollback hard disk changes */
11011 mMediaData.rollback();
11012
11013 MultiResult mrc(S_OK);
11014
11015 // Delete unused implicit diffs.
11016 if (implicitAtts.size() != 0)
11017 {
11018 alock.release();
11019
11020 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11021 {
11022 // Remove medium associated with this attachment.
11023 ComObjPtr<MediumAttachment> pAtt = *it;
11024 Assert(pAtt);
11025 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11026 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11027 Assert(pMedium);
11028
11029 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11030 // continue on delete failure, just collect error messages
11031 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11032 pMedium->i_getLocationFull().c_str() ));
11033 mrc = rc;
11034 }
11035 // Clear the list of deleted implicit attachments now, while not
11036 // holding the lock, as it will ultimately trigger Medium::uninit()
11037 // calls which assume that the media tree lock isn't held.
11038 implicitAtts.clear();
11039
11040 alock.acquire();
11041
11042 /* if there is a VM recreate media lock map as mentioned above,
11043 * otherwise it is a waste of time and we leave things unlocked */
11044 if (aOnline)
11045 {
11046 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11047 /* must never be NULL, but better safe than sorry */
11048 if (!pMachine.isNull())
11049 {
11050 alock.release();
11051 rc = mData->mSession.mMachine->i_lockMedia();
11052 alock.acquire();
11053 if (FAILED(rc))
11054 throw rc;
11055 }
11056 }
11057 }
11058 }
11059 catch (HRESULT aRC) {rc = aRC;}
11060
11061 if (mData->mMachineState == MachineState_SettingUp)
11062 i_setMachineState(oldState);
11063
11064 /* unlock all hard disks we locked when there is no VM */
11065 if (!aOnline)
11066 {
11067 ErrorInfoKeeper eik;
11068
11069 HRESULT rc1 = lockedMediaMap->Clear();
11070 AssertComRC(rc1);
11071 }
11072
11073 return rc;
11074}
11075
11076
11077/**
11078 * Looks through the given list of media attachments for one with the given parameters
11079 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11080 * can be searched as well if needed.
11081 *
11082 * @param list
11083 * @param aControllerName
11084 * @param aControllerPort
11085 * @param aDevice
11086 * @return
11087 */
11088MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11089 const Utf8Str &aControllerName,
11090 LONG aControllerPort,
11091 LONG aDevice)
11092{
11093 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11094 {
11095 MediumAttachment *pAttach = *it;
11096 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11097 return pAttach;
11098 }
11099
11100 return NULL;
11101}
11102
11103/**
11104 * Looks through the given list of media attachments for one with the given parameters
11105 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11106 * can be searched as well if needed.
11107 *
11108 * @param list
11109 * @param aControllerName
11110 * @param aControllerPort
11111 * @param aDevice
11112 * @return
11113 */
11114MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11115 ComObjPtr<Medium> pMedium)
11116{
11117 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11118 {
11119 MediumAttachment *pAttach = *it;
11120 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11121 if (pMediumThis == pMedium)
11122 return pAttach;
11123 }
11124
11125 return NULL;
11126}
11127
11128/**
11129 * Looks through the given list of media attachments for one with the given parameters
11130 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11131 * can be searched as well if needed.
11132 *
11133 * @param list
11134 * @param aControllerName
11135 * @param aControllerPort
11136 * @param aDevice
11137 * @return
11138 */
11139MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11140 Guid &id)
11141{
11142 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11143 {
11144 MediumAttachment *pAttach = *it;
11145 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11146 if (pMediumThis->i_getId() == id)
11147 return pAttach;
11148 }
11149
11150 return NULL;
11151}
11152
11153/**
11154 * Main implementation for Machine::DetachDevice. This also gets called
11155 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11156 *
11157 * @param pAttach Medium attachment to detach.
11158 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11159 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11160 * SnapshotMachine, and this must be its snapshot.
11161 * @return
11162 */
11163HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11164 AutoWriteLock &writeLock,
11165 Snapshot *pSnapshot)
11166{
11167 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11168 DeviceType_T mediumType = pAttach->i_getType();
11169
11170 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11171
11172 if (pAttach->i_isImplicit())
11173 {
11174 /* attempt to implicitly delete the implicitly created diff */
11175
11176 /// @todo move the implicit flag from MediumAttachment to Medium
11177 /// and forbid any hard disk operation when it is implicit. Or maybe
11178 /// a special media state for it to make it even more simple.
11179
11180 Assert(mMediaData.isBackedUp());
11181
11182 /* will release the lock before the potentially lengthy operation, so
11183 * protect with the special state */
11184 MachineState_T oldState = mData->mMachineState;
11185 i_setMachineState(MachineState_SettingUp);
11186
11187 writeLock.release();
11188
11189 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11190 true /*aWait*/);
11191
11192 writeLock.acquire();
11193
11194 i_setMachineState(oldState);
11195
11196 if (FAILED(rc)) return rc;
11197 }
11198
11199 i_setModified(IsModified_Storage);
11200 mMediaData.backup();
11201 mMediaData->mAttachments.remove(pAttach);
11202
11203 if (!oldmedium.isNull())
11204 {
11205 // if this is from a snapshot, do not defer detachment to commitMedia()
11206 if (pSnapshot)
11207 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11208 // else if non-hard disk media, do not defer detachment to commitMedia() either
11209 else if (mediumType != DeviceType_HardDisk)
11210 oldmedium->i_removeBackReference(mData->mUuid);
11211 }
11212
11213 return S_OK;
11214}
11215
11216/**
11217 * Goes thru all media of the given list and
11218 *
11219 * 1) calls i_detachDevice() on each of them for this machine and
11220 * 2) adds all Medium objects found in the process to the given list,
11221 * depending on cleanupMode.
11222 *
11223 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11224 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11225 * media to the list.
11226 *
11227 * This gets called from Machine::Unregister, both for the actual Machine and
11228 * the SnapshotMachine objects that might be found in the snapshots.
11229 *
11230 * Requires caller and locking. The machine lock must be passed in because it
11231 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11232 *
11233 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11234 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11235 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11236 * Full, then all media get added;
11237 * otherwise no media get added.
11238 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11239 * @return
11240 */
11241HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11242 Snapshot *pSnapshot,
11243 CleanupMode_T cleanupMode,
11244 MediaList &llMedia)
11245{
11246 Assert(isWriteLockOnCurrentThread());
11247
11248 HRESULT rc;
11249
11250 // make a temporary list because i_detachDevice invalidates iterators into
11251 // mMediaData->mAttachments
11252 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11253
11254 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11255 {
11256 ComObjPtr<MediumAttachment> &pAttach = *it;
11257 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11258
11259 if (!pMedium.isNull())
11260 {
11261 AutoCaller mac(pMedium);
11262 if (FAILED(mac.rc())) return mac.rc();
11263 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11264 DeviceType_T devType = pMedium->i_getDeviceType();
11265 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11266 && devType == DeviceType_HardDisk)
11267 || (cleanupMode == CleanupMode_Full)
11268 )
11269 {
11270 llMedia.push_back(pMedium);
11271 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11272 /* Not allowed to keep this lock as below we need the parent
11273 * medium lock, and the lock order is parent to child. */
11274 lock.release();
11275 /*
11276 * Search for medias which are not attached to any machine, but
11277 * in the chain to an attached disk. Mediums are only consided
11278 * if they are:
11279 * - have only one child
11280 * - no references to any machines
11281 * - are of normal medium type
11282 */
11283 while (!pParent.isNull())
11284 {
11285 AutoCaller mac1(pParent);
11286 if (FAILED(mac1.rc())) return mac1.rc();
11287 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11288 if (pParent->i_getChildren().size() == 1)
11289 {
11290 if ( pParent->i_getMachineBackRefCount() == 0
11291 && pParent->i_getType() == MediumType_Normal
11292 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11293 llMedia.push_back(pParent);
11294 }
11295 else
11296 break;
11297 pParent = pParent->i_getParent();
11298 }
11299 }
11300 }
11301
11302 // real machine: then we need to use the proper method
11303 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11304
11305 if (FAILED(rc))
11306 return rc;
11307 }
11308
11309 return S_OK;
11310}
11311
11312/**
11313 * Perform deferred hard disk detachments.
11314 *
11315 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11316 * backed up).
11317 *
11318 * If @a aOnline is @c true then this method will also unlock the old hard disks
11319 * for which the new implicit diffs were created and will lock these new diffs for
11320 * writing.
11321 *
11322 * @param aOnline Whether the VM was online prior to this operation.
11323 *
11324 * @note Locks this object for writing!
11325 */
11326void Machine::i_commitMedia(bool aOnline /*= false*/)
11327{
11328 AutoCaller autoCaller(this);
11329 AssertComRCReturnVoid(autoCaller.rc());
11330
11331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11332
11333 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11334
11335 HRESULT rc = S_OK;
11336
11337 /* no attach/detach operations -- nothing to do */
11338 if (!mMediaData.isBackedUp())
11339 return;
11340
11341 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11342 bool fMediaNeedsLocking = false;
11343
11344 /* enumerate new attachments */
11345 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11346 it != mMediaData->mAttachments.end();
11347 ++it)
11348 {
11349 MediumAttachment *pAttach = *it;
11350
11351 pAttach->i_commit();
11352
11353 Medium* pMedium = pAttach->i_getMedium();
11354 bool fImplicit = pAttach->i_isImplicit();
11355
11356 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11357 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11358 fImplicit));
11359
11360 /** @todo convert all this Machine-based voodoo to MediumAttachment
11361 * based commit logic. */
11362 if (fImplicit)
11363 {
11364 /* convert implicit attachment to normal */
11365 pAttach->i_setImplicit(false);
11366
11367 if ( aOnline
11368 && pMedium
11369 && pAttach->i_getType() == DeviceType_HardDisk
11370 )
11371 {
11372 /* update the appropriate lock list */
11373 MediumLockList *pMediumLockList;
11374 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11375 AssertComRC(rc);
11376 if (pMediumLockList)
11377 {
11378 /* unlock if there's a need to change the locking */
11379 if (!fMediaNeedsLocking)
11380 {
11381 rc = mData->mSession.mLockedMedia.Unlock();
11382 AssertComRC(rc);
11383 fMediaNeedsLocking = true;
11384 }
11385 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11386 AssertComRC(rc);
11387 rc = pMediumLockList->Append(pMedium, true);
11388 AssertComRC(rc);
11389 }
11390 }
11391
11392 continue;
11393 }
11394
11395 if (pMedium)
11396 {
11397 /* was this medium attached before? */
11398 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11399 {
11400 MediumAttachment *pOldAttach = *oldIt;
11401 if (pOldAttach->i_getMedium() == pMedium)
11402 {
11403 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11404
11405 /* yes: remove from old to avoid de-association */
11406 oldAtts.erase(oldIt);
11407 break;
11408 }
11409 }
11410 }
11411 }
11412
11413 /* enumerate remaining old attachments and de-associate from the
11414 * current machine state */
11415 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11416 {
11417 MediumAttachment *pAttach = *it;
11418 Medium* pMedium = pAttach->i_getMedium();
11419
11420 /* Detach only hard disks, since DVD/floppy media is detached
11421 * instantly in MountMedium. */
11422 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11423 {
11424 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11425
11426 /* now de-associate from the current machine state */
11427 rc = pMedium->i_removeBackReference(mData->mUuid);
11428 AssertComRC(rc);
11429
11430 if (aOnline)
11431 {
11432 /* unlock since medium is not used anymore */
11433 MediumLockList *pMediumLockList;
11434 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11435 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11436 {
11437 /* this happens for online snapshots, there the attachment
11438 * is changing, but only to a diff image created under
11439 * the old one, so there is no separate lock list */
11440 Assert(!pMediumLockList);
11441 }
11442 else
11443 {
11444 AssertComRC(rc);
11445 if (pMediumLockList)
11446 {
11447 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11448 AssertComRC(rc);
11449 }
11450 }
11451 }
11452 }
11453 }
11454
11455 /* take media locks again so that the locking state is consistent */
11456 if (fMediaNeedsLocking)
11457 {
11458 Assert(aOnline);
11459 rc = mData->mSession.mLockedMedia.Lock();
11460 AssertComRC(rc);
11461 }
11462
11463 /* commit the hard disk changes */
11464 mMediaData.commit();
11465
11466 if (i_isSessionMachine())
11467 {
11468 /*
11469 * Update the parent machine to point to the new owner.
11470 * This is necessary because the stored parent will point to the
11471 * session machine otherwise and cause crashes or errors later
11472 * when the session machine gets invalid.
11473 */
11474 /** @todo Change the MediumAttachment class to behave like any other
11475 * class in this regard by creating peer MediumAttachment
11476 * objects for session machines and share the data with the peer
11477 * machine.
11478 */
11479 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11480 it != mMediaData->mAttachments.end();
11481 ++it)
11482 (*it)->i_updateParentMachine(mPeer);
11483
11484 /* attach new data to the primary machine and reshare it */
11485 mPeer->mMediaData.attach(mMediaData);
11486 }
11487
11488 return;
11489}
11490
11491/**
11492 * Perform deferred deletion of implicitly created diffs.
11493 *
11494 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11495 * backed up).
11496 *
11497 * @note Locks this object for writing!
11498 */
11499void Machine::i_rollbackMedia()
11500{
11501 AutoCaller autoCaller(this);
11502 AssertComRCReturnVoid(autoCaller.rc());
11503
11504 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11505 LogFlowThisFunc(("Entering rollbackMedia\n"));
11506
11507 HRESULT rc = S_OK;
11508
11509 /* no attach/detach operations -- nothing to do */
11510 if (!mMediaData.isBackedUp())
11511 return;
11512
11513 /* enumerate new attachments */
11514 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11515 it != mMediaData->mAttachments.end();
11516 ++it)
11517 {
11518 MediumAttachment *pAttach = *it;
11519 /* Fix up the backrefs for DVD/floppy media. */
11520 if (pAttach->i_getType() != DeviceType_HardDisk)
11521 {
11522 Medium* pMedium = pAttach->i_getMedium();
11523 if (pMedium)
11524 {
11525 rc = pMedium->i_removeBackReference(mData->mUuid);
11526 AssertComRC(rc);
11527 }
11528 }
11529
11530 (*it)->i_rollback();
11531
11532 pAttach = *it;
11533 /* Fix up the backrefs for DVD/floppy media. */
11534 if (pAttach->i_getType() != DeviceType_HardDisk)
11535 {
11536 Medium* pMedium = pAttach->i_getMedium();
11537 if (pMedium)
11538 {
11539 rc = pMedium->i_addBackReference(mData->mUuid);
11540 AssertComRC(rc);
11541 }
11542 }
11543 }
11544
11545 /** @todo convert all this Machine-based voodoo to MediumAttachment
11546 * based rollback logic. */
11547 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11548
11549 return;
11550}
11551
11552/**
11553 * Returns true if the settings file is located in the directory named exactly
11554 * as the machine; this means, among other things, that the machine directory
11555 * should be auto-renamed.
11556 *
11557 * @param aSettingsDir if not NULL, the full machine settings file directory
11558 * name will be assigned there.
11559 *
11560 * @note Doesn't lock anything.
11561 * @note Not thread safe (must be called from this object's lock).
11562 */
11563bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11564{
11565 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11566 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11567 if (aSettingsDir)
11568 *aSettingsDir = strMachineDirName;
11569 strMachineDirName.stripPath(); // vmname
11570 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11571 strConfigFileOnly.stripPath() // vmname.vbox
11572 .stripSuffix(); // vmname
11573 /** @todo hack, make somehow use of ComposeMachineFilename */
11574 if (mUserData->s.fDirectoryIncludesUUID)
11575 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11576
11577 AssertReturn(!strMachineDirName.isEmpty(), false);
11578 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11579
11580 return strMachineDirName == strConfigFileOnly;
11581}
11582
11583/**
11584 * Discards all changes to machine settings.
11585 *
11586 * @param aNotify Whether to notify the direct session about changes or not.
11587 *
11588 * @note Locks objects for writing!
11589 */
11590void Machine::i_rollback(bool aNotify)
11591{
11592 AutoCaller autoCaller(this);
11593 AssertComRCReturn(autoCaller.rc(), (void)0);
11594
11595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11596
11597 if (!mStorageControllers.isNull())
11598 {
11599 if (mStorageControllers.isBackedUp())
11600 {
11601 /* unitialize all new devices (absent in the backed up list). */
11602 StorageControllerList::const_iterator it = mStorageControllers->begin();
11603 StorageControllerList *backedList = mStorageControllers.backedUpData();
11604 while (it != mStorageControllers->end())
11605 {
11606 if ( std::find(backedList->begin(), backedList->end(), *it)
11607 == backedList->end()
11608 )
11609 {
11610 (*it)->uninit();
11611 }
11612 ++it;
11613 }
11614
11615 /* restore the list */
11616 mStorageControllers.rollback();
11617 }
11618
11619 /* rollback any changes to devices after restoring the list */
11620 if (mData->flModifications & IsModified_Storage)
11621 {
11622 StorageControllerList::const_iterator it = mStorageControllers->begin();
11623 while (it != mStorageControllers->end())
11624 {
11625 (*it)->i_rollback();
11626 ++it;
11627 }
11628 }
11629 }
11630
11631 if (!mUSBControllers.isNull())
11632 {
11633 if (mUSBControllers.isBackedUp())
11634 {
11635 /* unitialize all new devices (absent in the backed up list). */
11636 USBControllerList::const_iterator it = mUSBControllers->begin();
11637 USBControllerList *backedList = mUSBControllers.backedUpData();
11638 while (it != mUSBControllers->end())
11639 {
11640 if ( std::find(backedList->begin(), backedList->end(), *it)
11641 == backedList->end()
11642 )
11643 {
11644 (*it)->uninit();
11645 }
11646 ++it;
11647 }
11648
11649 /* restore the list */
11650 mUSBControllers.rollback();
11651 }
11652
11653 /* rollback any changes to devices after restoring the list */
11654 if (mData->flModifications & IsModified_USB)
11655 {
11656 USBControllerList::const_iterator it = mUSBControllers->begin();
11657 while (it != mUSBControllers->end())
11658 {
11659 (*it)->i_rollback();
11660 ++it;
11661 }
11662 }
11663 }
11664
11665 mUserData.rollback();
11666
11667 mHWData.rollback();
11668
11669 if (mData->flModifications & IsModified_Storage)
11670 i_rollbackMedia();
11671
11672 if (mBIOSSettings)
11673 mBIOSSettings->i_rollback();
11674
11675 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11676 mVRDEServer->i_rollback();
11677
11678 if (mAudioAdapter)
11679 mAudioAdapter->i_rollback();
11680
11681 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11682 mUSBDeviceFilters->i_rollback();
11683
11684 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11685 mBandwidthControl->i_rollback();
11686
11687 if (!mHWData.isNull())
11688 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11689 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11690 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11691 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11692
11693 if (mData->flModifications & IsModified_NetworkAdapters)
11694 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11695 if ( mNetworkAdapters[slot]
11696 && mNetworkAdapters[slot]->i_isModified())
11697 {
11698 mNetworkAdapters[slot]->i_rollback();
11699 networkAdapters[slot] = mNetworkAdapters[slot];
11700 }
11701
11702 if (mData->flModifications & IsModified_SerialPorts)
11703 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11704 if ( mSerialPorts[slot]
11705 && mSerialPorts[slot]->i_isModified())
11706 {
11707 mSerialPorts[slot]->i_rollback();
11708 serialPorts[slot] = mSerialPorts[slot];
11709 }
11710
11711 if (mData->flModifications & IsModified_ParallelPorts)
11712 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11713 if ( mParallelPorts[slot]
11714 && mParallelPorts[slot]->i_isModified())
11715 {
11716 mParallelPorts[slot]->i_rollback();
11717 parallelPorts[slot] = mParallelPorts[slot];
11718 }
11719
11720 if (aNotify)
11721 {
11722 /* inform the direct session about changes */
11723
11724 ComObjPtr<Machine> that = this;
11725 uint32_t flModifications = mData->flModifications;
11726 alock.release();
11727
11728 if (flModifications & IsModified_SharedFolders)
11729 that->i_onSharedFolderChange();
11730
11731 if (flModifications & IsModified_VRDEServer)
11732 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11733 if (flModifications & IsModified_USB)
11734 that->i_onUSBControllerChange();
11735
11736 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11737 if (networkAdapters[slot])
11738 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11739 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11740 if (serialPorts[slot])
11741 that->i_onSerialPortChange(serialPorts[slot]);
11742 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11743 if (parallelPorts[slot])
11744 that->i_onParallelPortChange(parallelPorts[slot]);
11745
11746 if (flModifications & IsModified_Storage)
11747 that->i_onStorageControllerChange();
11748
11749#if 0
11750 if (flModifications & IsModified_BandwidthControl)
11751 that->onBandwidthControlChange();
11752#endif
11753 }
11754}
11755
11756/**
11757 * Commits all the changes to machine settings.
11758 *
11759 * Note that this operation is supposed to never fail.
11760 *
11761 * @note Locks this object and children for writing.
11762 */
11763void Machine::i_commit()
11764{
11765 AutoCaller autoCaller(this);
11766 AssertComRCReturnVoid(autoCaller.rc());
11767
11768 AutoCaller peerCaller(mPeer);
11769 AssertComRCReturnVoid(peerCaller.rc());
11770
11771 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11772
11773 /*
11774 * use safe commit to ensure Snapshot machines (that share mUserData)
11775 * will still refer to a valid memory location
11776 */
11777 mUserData.commitCopy();
11778
11779 mHWData.commit();
11780
11781 if (mMediaData.isBackedUp())
11782 i_commitMedia(Global::IsOnline(mData->mMachineState));
11783
11784 mBIOSSettings->i_commit();
11785 mVRDEServer->i_commit();
11786 mAudioAdapter->i_commit();
11787 mUSBDeviceFilters->i_commit();
11788 mBandwidthControl->i_commit();
11789
11790 /* Since mNetworkAdapters is a list which might have been changed (resized)
11791 * without using the Backupable<> template we need to handle the copying
11792 * of the list entries manually, including the creation of peers for the
11793 * new objects. */
11794 bool commitNetworkAdapters = false;
11795 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11796 if (mPeer)
11797 {
11798 /* commit everything, even the ones which will go away */
11799 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11800 mNetworkAdapters[slot]->i_commit();
11801 /* copy over the new entries, creating a peer and uninit the original */
11802 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11803 for (size_t slot = 0; slot < newSize; slot++)
11804 {
11805 /* look if this adapter has a peer device */
11806 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11807 if (!peer)
11808 {
11809 /* no peer means the adapter is a newly created one;
11810 * create a peer owning data this data share it with */
11811 peer.createObject();
11812 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11813 }
11814 mPeer->mNetworkAdapters[slot] = peer;
11815 }
11816 /* uninit any no longer needed network adapters */
11817 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11818 mNetworkAdapters[slot]->uninit();
11819 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11820 {
11821 if (mPeer->mNetworkAdapters[slot])
11822 mPeer->mNetworkAdapters[slot]->uninit();
11823 }
11824 /* Keep the original network adapter count until this point, so that
11825 * discarding a chipset type change will not lose settings. */
11826 mNetworkAdapters.resize(newSize);
11827 mPeer->mNetworkAdapters.resize(newSize);
11828 }
11829 else
11830 {
11831 /* we have no peer (our parent is the newly created machine);
11832 * just commit changes to the network adapters */
11833 commitNetworkAdapters = true;
11834 }
11835 if (commitNetworkAdapters)
11836 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11837 mNetworkAdapters[slot]->i_commit();
11838
11839 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11840 mSerialPorts[slot]->i_commit();
11841 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11842 mParallelPorts[slot]->i_commit();
11843
11844 bool commitStorageControllers = false;
11845
11846 if (mStorageControllers.isBackedUp())
11847 {
11848 mStorageControllers.commit();
11849
11850 if (mPeer)
11851 {
11852 /* Commit all changes to new controllers (this will reshare data with
11853 * peers for those who have peers) */
11854 StorageControllerList *newList = new StorageControllerList();
11855 StorageControllerList::const_iterator it = mStorageControllers->begin();
11856 while (it != mStorageControllers->end())
11857 {
11858 (*it)->i_commit();
11859
11860 /* look if this controller has a peer device */
11861 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11862 if (!peer)
11863 {
11864 /* no peer means the device is a newly created one;
11865 * create a peer owning data this device share it with */
11866 peer.createObject();
11867 peer->init(mPeer, *it, true /* aReshare */);
11868 }
11869 else
11870 {
11871 /* remove peer from the old list */
11872 mPeer->mStorageControllers->remove(peer);
11873 }
11874 /* and add it to the new list */
11875 newList->push_back(peer);
11876
11877 ++it;
11878 }
11879
11880 /* uninit old peer's controllers that are left */
11881 it = mPeer->mStorageControllers->begin();
11882 while (it != mPeer->mStorageControllers->end())
11883 {
11884 (*it)->uninit();
11885 ++it;
11886 }
11887
11888 /* attach new list of controllers to our peer */
11889 mPeer->mStorageControllers.attach(newList);
11890 }
11891 else
11892 {
11893 /* we have no peer (our parent is the newly created machine);
11894 * just commit changes to devices */
11895 commitStorageControllers = true;
11896 }
11897 }
11898 else
11899 {
11900 /* the list of controllers itself is not changed,
11901 * just commit changes to controllers themselves */
11902 commitStorageControllers = true;
11903 }
11904
11905 if (commitStorageControllers)
11906 {
11907 StorageControllerList::const_iterator it = mStorageControllers->begin();
11908 while (it != mStorageControllers->end())
11909 {
11910 (*it)->i_commit();
11911 ++it;
11912 }
11913 }
11914
11915 bool commitUSBControllers = false;
11916
11917 if (mUSBControllers.isBackedUp())
11918 {
11919 mUSBControllers.commit();
11920
11921 if (mPeer)
11922 {
11923 /* Commit all changes to new controllers (this will reshare data with
11924 * peers for those who have peers) */
11925 USBControllerList *newList = new USBControllerList();
11926 USBControllerList::const_iterator it = mUSBControllers->begin();
11927 while (it != mUSBControllers->end())
11928 {
11929 (*it)->i_commit();
11930
11931 /* look if this controller has a peer device */
11932 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11933 if (!peer)
11934 {
11935 /* no peer means the device is a newly created one;
11936 * create a peer owning data this device share it with */
11937 peer.createObject();
11938 peer->init(mPeer, *it, true /* aReshare */);
11939 }
11940 else
11941 {
11942 /* remove peer from the old list */
11943 mPeer->mUSBControllers->remove(peer);
11944 }
11945 /* and add it to the new list */
11946 newList->push_back(peer);
11947
11948 ++it;
11949 }
11950
11951 /* uninit old peer's controllers that are left */
11952 it = mPeer->mUSBControllers->begin();
11953 while (it != mPeer->mUSBControllers->end())
11954 {
11955 (*it)->uninit();
11956 ++it;
11957 }
11958
11959 /* attach new list of controllers to our peer */
11960 mPeer->mUSBControllers.attach(newList);
11961 }
11962 else
11963 {
11964 /* we have no peer (our parent is the newly created machine);
11965 * just commit changes to devices */
11966 commitUSBControllers = true;
11967 }
11968 }
11969 else
11970 {
11971 /* the list of controllers itself is not changed,
11972 * just commit changes to controllers themselves */
11973 commitUSBControllers = true;
11974 }
11975
11976 if (commitUSBControllers)
11977 {
11978 USBControllerList::const_iterator it = mUSBControllers->begin();
11979 while (it != mUSBControllers->end())
11980 {
11981 (*it)->i_commit();
11982 ++it;
11983 }
11984 }
11985
11986 if (i_isSessionMachine())
11987 {
11988 /* attach new data to the primary machine and reshare it */
11989 mPeer->mUserData.attach(mUserData);
11990 mPeer->mHWData.attach(mHWData);
11991 /* mMediaData is reshared by fixupMedia */
11992 // mPeer->mMediaData.attach(mMediaData);
11993 Assert(mPeer->mMediaData.data() == mMediaData.data());
11994 }
11995}
11996
11997/**
11998 * Copies all the hardware data from the given machine.
11999 *
12000 * Currently, only called when the VM is being restored from a snapshot. In
12001 * particular, this implies that the VM is not running during this method's
12002 * call.
12003 *
12004 * @note This method must be called from under this object's lock.
12005 *
12006 * @note This method doesn't call #commit(), so all data remains backed up and
12007 * unsaved.
12008 */
12009void Machine::i_copyFrom(Machine *aThat)
12010{
12011 AssertReturnVoid(!i_isSnapshotMachine());
12012 AssertReturnVoid(aThat->i_isSnapshotMachine());
12013
12014 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12015
12016 mHWData.assignCopy(aThat->mHWData);
12017
12018 // create copies of all shared folders (mHWData after attaching a copy
12019 // contains just references to original objects)
12020 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12021 it != mHWData->mSharedFolders.end();
12022 ++it)
12023 {
12024 ComObjPtr<SharedFolder> folder;
12025 folder.createObject();
12026 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12027 AssertComRC(rc);
12028 *it = folder;
12029 }
12030
12031 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12032 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12033 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12034 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12035 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12036
12037 /* create private copies of all controllers */
12038 mStorageControllers.backup();
12039 mStorageControllers->clear();
12040 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12041 it != aThat->mStorageControllers->end();
12042 ++it)
12043 {
12044 ComObjPtr<StorageController> ctrl;
12045 ctrl.createObject();
12046 ctrl->initCopy(this, *it);
12047 mStorageControllers->push_back(ctrl);
12048 }
12049
12050 /* create private copies of all USB controllers */
12051 mUSBControllers.backup();
12052 mUSBControllers->clear();
12053 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12054 it != aThat->mUSBControllers->end();
12055 ++it)
12056 {
12057 ComObjPtr<USBController> ctrl;
12058 ctrl.createObject();
12059 ctrl->initCopy(this, *it);
12060 mUSBControllers->push_back(ctrl);
12061 }
12062
12063 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12064 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12065 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12066 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12067 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12068 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12069 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12070}
12071
12072/**
12073 * Returns whether the given storage controller is hotplug capable.
12074 *
12075 * @returns true if the controller supports hotplugging
12076 * false otherwise.
12077 * @param enmCtrlType The controller type to check for.
12078 */
12079bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12080{
12081 ComPtr<ISystemProperties> systemProperties;
12082 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12083 if (FAILED(rc))
12084 return false;
12085
12086 BOOL aHotplugCapable = FALSE;
12087 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12088
12089 return RT_BOOL(aHotplugCapable);
12090}
12091
12092#ifdef VBOX_WITH_RESOURCE_USAGE_API
12093
12094void Machine::i_getDiskList(MediaList &list)
12095{
12096 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12097 it != mMediaData->mAttachments.end();
12098 ++it)
12099 {
12100 MediumAttachment* pAttach = *it;
12101 /* just in case */
12102 AssertStmt(pAttach, continue);
12103
12104 AutoCaller localAutoCallerA(pAttach);
12105 if (FAILED(localAutoCallerA.rc())) continue;
12106
12107 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12108
12109 if (pAttach->i_getType() == DeviceType_HardDisk)
12110 list.push_back(pAttach->i_getMedium());
12111 }
12112}
12113
12114void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12115{
12116 AssertReturnVoid(isWriteLockOnCurrentThread());
12117 AssertPtrReturnVoid(aCollector);
12118
12119 pm::CollectorHAL *hal = aCollector->getHAL();
12120 /* Create sub metrics */
12121 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12122 "Percentage of processor time spent in user mode by the VM process.");
12123 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12124 "Percentage of processor time spent in kernel mode by the VM process.");
12125 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12126 "Size of resident portion of VM process in memory.");
12127 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12128 "Actual size of all VM disks combined.");
12129 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12130 "Network receive rate.");
12131 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12132 "Network transmit rate.");
12133 /* Create and register base metrics */
12134 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12135 cpuLoadUser, cpuLoadKernel);
12136 aCollector->registerBaseMetric(cpuLoad);
12137 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12138 ramUsageUsed);
12139 aCollector->registerBaseMetric(ramUsage);
12140 MediaList disks;
12141 i_getDiskList(disks);
12142 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12143 diskUsageUsed);
12144 aCollector->registerBaseMetric(diskUsage);
12145
12146 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12147 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12148 new pm::AggregateAvg()));
12149 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12150 new pm::AggregateMin()));
12151 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12152 new pm::AggregateMax()));
12153 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12154 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12155 new pm::AggregateAvg()));
12156 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12157 new pm::AggregateMin()));
12158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12159 new pm::AggregateMax()));
12160
12161 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12162 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12163 new pm::AggregateAvg()));
12164 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12165 new pm::AggregateMin()));
12166 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12167 new pm::AggregateMax()));
12168
12169 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12170 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12171 new pm::AggregateAvg()));
12172 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12173 new pm::AggregateMin()));
12174 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12175 new pm::AggregateMax()));
12176
12177
12178 /* Guest metrics collector */
12179 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12180 aCollector->registerGuest(mCollectorGuest);
12181 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12182
12183 /* Create sub metrics */
12184 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12185 "Percentage of processor time spent in user mode as seen by the guest.");
12186 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12187 "Percentage of processor time spent in kernel mode as seen by the guest.");
12188 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12189 "Percentage of processor time spent idling as seen by the guest.");
12190
12191 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12192 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12193 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12194 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12195 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12196 pm::SubMetric *guestMemCache = new pm::SubMetric(
12197 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12198
12199 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12200 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12201
12202 /* Create and register base metrics */
12203 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12204 machineNetRx, machineNetTx);
12205 aCollector->registerBaseMetric(machineNetRate);
12206
12207 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12208 guestLoadUser, guestLoadKernel, guestLoadIdle);
12209 aCollector->registerBaseMetric(guestCpuLoad);
12210
12211 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12212 guestMemTotal, guestMemFree,
12213 guestMemBalloon, guestMemShared,
12214 guestMemCache, guestPagedTotal);
12215 aCollector->registerBaseMetric(guestCpuMem);
12216
12217 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12218 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12223 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12233 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12261
12262 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12266
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12271}
12272
12273void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12274{
12275 AssertReturnVoid(isWriteLockOnCurrentThread());
12276
12277 if (aCollector)
12278 {
12279 aCollector->unregisterMetricsFor(aMachine);
12280 aCollector->unregisterBaseMetricsFor(aMachine);
12281 }
12282}
12283
12284#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12285
12286
12287////////////////////////////////////////////////////////////////////////////////
12288
12289DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12290
12291HRESULT SessionMachine::FinalConstruct()
12292{
12293 LogFlowThisFunc(("\n"));
12294
12295 mClientToken = NULL;
12296
12297 return BaseFinalConstruct();
12298}
12299
12300void SessionMachine::FinalRelease()
12301{
12302 LogFlowThisFunc(("\n"));
12303
12304 Assert(!mClientToken);
12305 /* paranoia, should not hang around any more */
12306 if (mClientToken)
12307 {
12308 delete mClientToken;
12309 mClientToken = NULL;
12310 }
12311
12312 uninit(Uninit::Unexpected);
12313
12314 BaseFinalRelease();
12315}
12316
12317/**
12318 * @note Must be called only by Machine::LockMachine() from its own write lock.
12319 */
12320HRESULT SessionMachine::init(Machine *aMachine)
12321{
12322 LogFlowThisFuncEnter();
12323 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12324
12325 AssertReturn(aMachine, E_INVALIDARG);
12326
12327 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12328
12329 /* Enclose the state transition NotReady->InInit->Ready */
12330 AutoInitSpan autoInitSpan(this);
12331 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12332
12333 HRESULT rc = S_OK;
12334
12335 RT_ZERO(mAuthLibCtx);
12336
12337 /* create the machine client token */
12338 try
12339 {
12340 mClientToken = new ClientToken(aMachine, this);
12341 if (!mClientToken->isReady())
12342 {
12343 delete mClientToken;
12344 mClientToken = NULL;
12345 rc = E_FAIL;
12346 }
12347 }
12348 catch (std::bad_alloc &)
12349 {
12350 rc = E_OUTOFMEMORY;
12351 }
12352 if (FAILED(rc))
12353 return rc;
12354
12355 /* memorize the peer Machine */
12356 unconst(mPeer) = aMachine;
12357 /* share the parent pointer */
12358 unconst(mParent) = aMachine->mParent;
12359
12360 /* take the pointers to data to share */
12361 mData.share(aMachine->mData);
12362 mSSData.share(aMachine->mSSData);
12363
12364 mUserData.share(aMachine->mUserData);
12365 mHWData.share(aMachine->mHWData);
12366 mMediaData.share(aMachine->mMediaData);
12367
12368 mStorageControllers.allocate();
12369 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12370 it != aMachine->mStorageControllers->end();
12371 ++it)
12372 {
12373 ComObjPtr<StorageController> ctl;
12374 ctl.createObject();
12375 ctl->init(this, *it);
12376 mStorageControllers->push_back(ctl);
12377 }
12378
12379 mUSBControllers.allocate();
12380 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12381 it != aMachine->mUSBControllers->end();
12382 ++it)
12383 {
12384 ComObjPtr<USBController> ctl;
12385 ctl.createObject();
12386 ctl->init(this, *it);
12387 mUSBControllers->push_back(ctl);
12388 }
12389
12390 unconst(mBIOSSettings).createObject();
12391 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12392 /* create another VRDEServer object that will be mutable */
12393 unconst(mVRDEServer).createObject();
12394 mVRDEServer->init(this, aMachine->mVRDEServer);
12395 /* create another audio adapter object that will be mutable */
12396 unconst(mAudioAdapter).createObject();
12397 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12398 /* create a list of serial ports that will be mutable */
12399 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12400 {
12401 unconst(mSerialPorts[slot]).createObject();
12402 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12403 }
12404 /* create a list of parallel ports that will be mutable */
12405 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12406 {
12407 unconst(mParallelPorts[slot]).createObject();
12408 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12409 }
12410
12411 /* create another USB device filters object that will be mutable */
12412 unconst(mUSBDeviceFilters).createObject();
12413 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12414
12415 /* create a list of network adapters that will be mutable */
12416 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12417 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12418 {
12419 unconst(mNetworkAdapters[slot]).createObject();
12420 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12421 }
12422
12423 /* create another bandwidth control object that will be mutable */
12424 unconst(mBandwidthControl).createObject();
12425 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12426
12427 /* default is to delete saved state on Saved -> PoweredOff transition */
12428 mRemoveSavedState = true;
12429
12430 /* Confirm a successful initialization when it's the case */
12431 autoInitSpan.setSucceeded();
12432
12433 miNATNetworksStarted = 0;
12434
12435 LogFlowThisFuncLeave();
12436 return rc;
12437}
12438
12439/**
12440 * Uninitializes this session object. If the reason is other than
12441 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12442 * or the client watcher code.
12443 *
12444 * @param aReason uninitialization reason
12445 *
12446 * @note Locks mParent + this object for writing.
12447 */
12448void SessionMachine::uninit(Uninit::Reason aReason)
12449{
12450 LogFlowThisFuncEnter();
12451 LogFlowThisFunc(("reason=%d\n", aReason));
12452
12453 /*
12454 * Strongly reference ourselves to prevent this object deletion after
12455 * mData->mSession.mMachine.setNull() below (which can release the last
12456 * reference and call the destructor). Important: this must be done before
12457 * accessing any members (and before AutoUninitSpan that does it as well).
12458 * This self reference will be released as the very last step on return.
12459 */
12460 ComObjPtr<SessionMachine> selfRef = this;
12461
12462 /* Enclose the state transition Ready->InUninit->NotReady */
12463 AutoUninitSpan autoUninitSpan(this);
12464 if (autoUninitSpan.uninitDone())
12465 {
12466 LogFlowThisFunc(("Already uninitialized\n"));
12467 LogFlowThisFuncLeave();
12468 return;
12469 }
12470
12471 if (autoUninitSpan.initFailed())
12472 {
12473 /* We've been called by init() because it's failed. It's not really
12474 * necessary (nor it's safe) to perform the regular uninit sequence
12475 * below, the following is enough.
12476 */
12477 LogFlowThisFunc(("Initialization failed.\n"));
12478 /* destroy the machine client token */
12479 if (mClientToken)
12480 {
12481 delete mClientToken;
12482 mClientToken = NULL;
12483 }
12484 uninitDataAndChildObjects();
12485 mData.free();
12486 unconst(mParent) = NULL;
12487 unconst(mPeer) = NULL;
12488 LogFlowThisFuncLeave();
12489 return;
12490 }
12491
12492 MachineState_T lastState;
12493 {
12494 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12495 lastState = mData->mMachineState;
12496 }
12497 NOREF(lastState);
12498
12499#ifdef VBOX_WITH_USB
12500 // release all captured USB devices, but do this before requesting the locks below
12501 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12502 {
12503 /* Console::captureUSBDevices() is called in the VM process only after
12504 * setting the machine state to Starting or Restoring.
12505 * Console::detachAllUSBDevices() will be called upon successful
12506 * termination. So, we need to release USB devices only if there was
12507 * an abnormal termination of a running VM.
12508 *
12509 * This is identical to SessionMachine::DetachAllUSBDevices except
12510 * for the aAbnormal argument. */
12511 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12512 AssertComRC(rc);
12513 NOREF(rc);
12514
12515 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12516 if (service)
12517 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12518 }
12519#endif /* VBOX_WITH_USB */
12520
12521 // we need to lock this object in uninit() because the lock is shared
12522 // with mPeer (as well as data we modify below). mParent lock is needed
12523 // by several calls to it, and USB needs host lock.
12524 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12525
12526#ifdef VBOX_WITH_RESOURCE_USAGE_API
12527 /*
12528 * It is safe to call Machine::i_unregisterMetrics() here because
12529 * PerformanceCollector::samplerCallback no longer accesses guest methods
12530 * holding the lock.
12531 */
12532 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12533 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12534 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12535 if (mCollectorGuest)
12536 {
12537 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12538 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12539 mCollectorGuest = NULL;
12540 }
12541#endif
12542
12543 if (aReason == Uninit::Abnormal)
12544 {
12545 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12546
12547 /* reset the state to Aborted */
12548 if (mData->mMachineState != MachineState_Aborted)
12549 i_setMachineState(MachineState_Aborted);
12550 }
12551
12552 // any machine settings modified?
12553 if (mData->flModifications)
12554 {
12555 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12556 i_rollback(false /* aNotify */);
12557 }
12558
12559 mData->mSession.mPID = NIL_RTPROCESS;
12560
12561 if (aReason == Uninit::Unexpected)
12562 {
12563 /* Uninitialization didn't come from #checkForDeath(), so tell the
12564 * client watcher thread to update the set of machines that have open
12565 * sessions. */
12566 mParent->i_updateClientWatcher();
12567 }
12568
12569 /* uninitialize all remote controls */
12570 if (mData->mSession.mRemoteControls.size())
12571 {
12572 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12573 mData->mSession.mRemoteControls.size()));
12574
12575 Data::Session::RemoteControlList::iterator it =
12576 mData->mSession.mRemoteControls.begin();
12577 while (it != mData->mSession.mRemoteControls.end())
12578 {
12579 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12580 HRESULT rc = (*it)->Uninitialize();
12581 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12582 if (FAILED(rc))
12583 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12584 ++it;
12585 }
12586 mData->mSession.mRemoteControls.clear();
12587 }
12588
12589 /* Remove all references to the NAT network service. The service will stop
12590 * if all references (also from other VMs) are removed. */
12591 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12592 {
12593 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12594 {
12595 NetworkAttachmentType_T type;
12596 HRESULT hrc;
12597
12598 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12599 if ( SUCCEEDED(hrc)
12600 && type == NetworkAttachmentType_NATNetwork)
12601 {
12602 Bstr name;
12603 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12604 if (SUCCEEDED(hrc))
12605 {
12606 multilock.release();
12607 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12608 mUserData->s.strName.c_str(), name.raw()));
12609 mParent->i_natNetworkRefDec(name.raw());
12610 multilock.acquire();
12611 }
12612 }
12613 }
12614 }
12615
12616 /*
12617 * An expected uninitialization can come only from #checkForDeath().
12618 * Otherwise it means that something's gone really wrong (for example,
12619 * the Session implementation has released the VirtualBox reference
12620 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12621 * etc). However, it's also possible, that the client releases the IPC
12622 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12623 * but the VirtualBox release event comes first to the server process.
12624 * This case is practically possible, so we should not assert on an
12625 * unexpected uninit, just log a warning.
12626 */
12627
12628 if ((aReason == Uninit::Unexpected))
12629 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12630
12631 if (aReason != Uninit::Normal)
12632 {
12633 mData->mSession.mDirectControl.setNull();
12634 }
12635 else
12636 {
12637 /* this must be null here (see #OnSessionEnd()) */
12638 Assert(mData->mSession.mDirectControl.isNull());
12639 Assert(mData->mSession.mState == SessionState_Unlocking);
12640 Assert(!mData->mSession.mProgress.isNull());
12641 }
12642 if (mData->mSession.mProgress)
12643 {
12644 if (aReason == Uninit::Normal)
12645 mData->mSession.mProgress->i_notifyComplete(S_OK);
12646 else
12647 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12648 COM_IIDOF(ISession),
12649 getComponentName(),
12650 tr("The VM session was aborted"));
12651 mData->mSession.mProgress.setNull();
12652 }
12653
12654 /* remove the association between the peer machine and this session machine */
12655 Assert( (SessionMachine*)mData->mSession.mMachine == this
12656 || aReason == Uninit::Unexpected);
12657
12658 /* reset the rest of session data */
12659 mData->mSession.mLockType = LockType_Null;
12660 mData->mSession.mMachine.setNull();
12661 mData->mSession.mState = SessionState_Unlocked;
12662 mData->mSession.mName.setNull();
12663
12664 /* destroy the machine client token before leaving the exclusive lock */
12665 if (mClientToken)
12666 {
12667 delete mClientToken;
12668 mClientToken = NULL;
12669 }
12670
12671 /* fire an event */
12672 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12673
12674 uninitDataAndChildObjects();
12675
12676 /* free the essential data structure last */
12677 mData.free();
12678
12679 /* release the exclusive lock before setting the below two to NULL */
12680 multilock.release();
12681
12682 unconst(mParent) = NULL;
12683 unconst(mPeer) = NULL;
12684
12685 AuthLibUnload(&mAuthLibCtx);
12686
12687 LogFlowThisFuncLeave();
12688}
12689
12690// util::Lockable interface
12691////////////////////////////////////////////////////////////////////////////////
12692
12693/**
12694 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12695 * with the primary Machine instance (mPeer).
12696 */
12697RWLockHandle *SessionMachine::lockHandle() const
12698{
12699 AssertReturn(mPeer != NULL, NULL);
12700 return mPeer->lockHandle();
12701}
12702
12703// IInternalMachineControl methods
12704////////////////////////////////////////////////////////////////////////////////
12705
12706/**
12707 * Passes collected guest statistics to performance collector object
12708 */
12709HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12710 ULONG aCpuKernel, ULONG aCpuIdle,
12711 ULONG aMemTotal, ULONG aMemFree,
12712 ULONG aMemBalloon, ULONG aMemShared,
12713 ULONG aMemCache, ULONG aPageTotal,
12714 ULONG aAllocVMM, ULONG aFreeVMM,
12715 ULONG aBalloonedVMM, ULONG aSharedVMM,
12716 ULONG aVmNetRx, ULONG aVmNetTx)
12717{
12718#ifdef VBOX_WITH_RESOURCE_USAGE_API
12719 if (mCollectorGuest)
12720 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12721 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12722 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12723 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12724
12725 return S_OK;
12726#else
12727 NOREF(aValidStats);
12728 NOREF(aCpuUser);
12729 NOREF(aCpuKernel);
12730 NOREF(aCpuIdle);
12731 NOREF(aMemTotal);
12732 NOREF(aMemFree);
12733 NOREF(aMemBalloon);
12734 NOREF(aMemShared);
12735 NOREF(aMemCache);
12736 NOREF(aPageTotal);
12737 NOREF(aAllocVMM);
12738 NOREF(aFreeVMM);
12739 NOREF(aBalloonedVMM);
12740 NOREF(aSharedVMM);
12741 NOREF(aVmNetRx);
12742 NOREF(aVmNetTx);
12743 return E_NOTIMPL;
12744#endif
12745}
12746
12747////////////////////////////////////////////////////////////////////////////////
12748//
12749// SessionMachine task records
12750//
12751////////////////////////////////////////////////////////////////////////////////
12752
12753/**
12754 * Task record for saving the machine state.
12755 */
12756struct SessionMachine::SaveStateTask
12757 : public Machine::Task
12758{
12759 SaveStateTask(SessionMachine *m,
12760 Progress *p,
12761 const Utf8Str &t,
12762 Reason_T enmReason,
12763 const Utf8Str &strStateFilePath)
12764 : Task(m, p, t),
12765 m_enmReason(enmReason),
12766 m_strStateFilePath(strStateFilePath)
12767 {}
12768
12769 void handler()
12770 {
12771 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12772 }
12773
12774 Reason_T m_enmReason;
12775 Utf8Str m_strStateFilePath;
12776};
12777
12778/**
12779 * Task thread implementation for SessionMachine::SaveState(), called from
12780 * SessionMachine::taskHandler().
12781 *
12782 * @note Locks this object for writing.
12783 *
12784 * @param task
12785 * @return
12786 */
12787void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12788{
12789 LogFlowThisFuncEnter();
12790
12791 AutoCaller autoCaller(this);
12792 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12793 if (FAILED(autoCaller.rc()))
12794 {
12795 /* we might have been uninitialized because the session was accidentally
12796 * closed by the client, so don't assert */
12797 HRESULT rc = setError(E_FAIL,
12798 tr("The session has been accidentally closed"));
12799 task.m_pProgress->i_notifyComplete(rc);
12800 LogFlowThisFuncLeave();
12801 return;
12802 }
12803
12804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12805
12806 HRESULT rc = S_OK;
12807
12808 try
12809 {
12810 ComPtr<IInternalSessionControl> directControl;
12811 if (mData->mSession.mLockType == LockType_VM)
12812 directControl = mData->mSession.mDirectControl;
12813 if (directControl.isNull())
12814 throw setError(VBOX_E_INVALID_VM_STATE,
12815 tr("Trying to save state without a running VM"));
12816 alock.release();
12817 BOOL fSuspendedBySave;
12818 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12819 Assert(!fSuspendedBySave);
12820 alock.acquire();
12821
12822 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12823 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12824 throw E_FAIL);
12825
12826 if (SUCCEEDED(rc))
12827 {
12828 mSSData->strStateFilePath = task.m_strStateFilePath;
12829
12830 /* save all VM settings */
12831 rc = i_saveSettings(NULL);
12832 // no need to check whether VirtualBox.xml needs saving also since
12833 // we can't have a name change pending at this point
12834 }
12835 else
12836 {
12837 // On failure, set the state to the state we had at the beginning.
12838 i_setMachineState(task.m_machineStateBackup);
12839 i_updateMachineStateOnClient();
12840
12841 // Delete the saved state file (might have been already created).
12842 // No need to check whether this is shared with a snapshot here
12843 // because we certainly created a fresh saved state file here.
12844 RTFileDelete(task.m_strStateFilePath.c_str());
12845 }
12846 }
12847 catch (HRESULT aRC) { rc = aRC; }
12848
12849 task.m_pProgress->i_notifyComplete(rc);
12850
12851 LogFlowThisFuncLeave();
12852}
12853
12854/**
12855 * @note Locks this object for writing.
12856 */
12857HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12858{
12859 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12860}
12861
12862HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12863{
12864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12865
12866 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12867 if (FAILED(rc)) return rc;
12868
12869 if ( mData->mMachineState != MachineState_Running
12870 && mData->mMachineState != MachineState_Paused
12871 )
12872 return setError(VBOX_E_INVALID_VM_STATE,
12873 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12874 Global::stringifyMachineState(mData->mMachineState));
12875
12876 ComObjPtr<Progress> pProgress;
12877 pProgress.createObject();
12878 rc = pProgress->init(i_getVirtualBox(),
12879 static_cast<IMachine *>(this) /* aInitiator */,
12880 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12881 FALSE /* aCancelable */);
12882 if (FAILED(rc))
12883 return rc;
12884
12885 Utf8Str strStateFilePath;
12886 i_composeSavedStateFilename(strStateFilePath);
12887
12888 /* create and start the task on a separate thread (note that it will not
12889 * start working until we release alock) */
12890 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12891 rc = pTask->createThread();
12892 if (FAILED(rc))
12893 return rc;
12894
12895 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12896 i_setMachineState(MachineState_Saving);
12897 i_updateMachineStateOnClient();
12898
12899 pProgress.queryInterfaceTo(aProgress.asOutParam());
12900
12901 return S_OK;
12902}
12903
12904/**
12905 * @note Locks this object for writing.
12906 */
12907HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12908{
12909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12910
12911 HRESULT rc = i_checkStateDependency(MutableStateDep);
12912 if (FAILED(rc)) return rc;
12913
12914 if ( mData->mMachineState != MachineState_PoweredOff
12915 && mData->mMachineState != MachineState_Teleported
12916 && mData->mMachineState != MachineState_Aborted
12917 )
12918 return setError(VBOX_E_INVALID_VM_STATE,
12919 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12920 Global::stringifyMachineState(mData->mMachineState));
12921
12922 com::Utf8Str stateFilePathFull;
12923 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12924 if (RT_FAILURE(vrc))
12925 return setError(VBOX_E_FILE_ERROR,
12926 tr("Invalid saved state file path '%s' (%Rrc)"),
12927 aSavedStateFile.c_str(),
12928 vrc);
12929
12930 mSSData->strStateFilePath = stateFilePathFull;
12931
12932 /* The below i_setMachineState() will detect the state transition and will
12933 * update the settings file */
12934
12935 return i_setMachineState(MachineState_Saved);
12936}
12937
12938/**
12939 * @note Locks this object for writing.
12940 */
12941HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12942{
12943 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12944
12945 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12946 if (FAILED(rc)) return rc;
12947
12948 if (mData->mMachineState != MachineState_Saved)
12949 return setError(VBOX_E_INVALID_VM_STATE,
12950 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12951 Global::stringifyMachineState(mData->mMachineState));
12952
12953 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12954
12955 /*
12956 * Saved -> PoweredOff transition will be detected in the SessionMachine
12957 * and properly handled.
12958 */
12959 rc = i_setMachineState(MachineState_PoweredOff);
12960 return rc;
12961}
12962
12963
12964/**
12965 * @note Locks the same as #i_setMachineState() does.
12966 */
12967HRESULT SessionMachine::updateState(MachineState_T aState)
12968{
12969 return i_setMachineState(aState);
12970}
12971
12972/**
12973 * @note Locks this object for writing.
12974 */
12975HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12976{
12977 IProgress* pProgress(aProgress);
12978
12979 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12980
12981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12982
12983 if (mData->mSession.mState != SessionState_Locked)
12984 return VBOX_E_INVALID_OBJECT_STATE;
12985
12986 if (!mData->mSession.mProgress.isNull())
12987 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12988
12989 /* If we didn't reference the NAT network service yet, add a reference to
12990 * force a start */
12991 if (miNATNetworksStarted < 1)
12992 {
12993 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12994 {
12995 NetworkAttachmentType_T type;
12996 HRESULT hrc;
12997 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12998 if ( SUCCEEDED(hrc)
12999 && type == NetworkAttachmentType_NATNetwork)
13000 {
13001 Bstr name;
13002 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13003 if (SUCCEEDED(hrc))
13004 {
13005 LogRel(("VM '%s' starts using NAT network '%ls'\n",
13006 mUserData->s.strName.c_str(), name.raw()));
13007 mPeer->lockHandle()->unlockWrite();
13008 mParent->i_natNetworkRefInc(name.raw());
13009#ifdef RT_LOCK_STRICT
13010 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13011#else
13012 mPeer->lockHandle()->lockWrite();
13013#endif
13014 }
13015 }
13016 }
13017 miNATNetworksStarted++;
13018 }
13019
13020 LogFlowThisFunc(("returns S_OK.\n"));
13021 return S_OK;
13022}
13023
13024/**
13025 * @note Locks this object for writing.
13026 */
13027HRESULT SessionMachine::endPowerUp(LONG aResult)
13028{
13029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13030
13031 if (mData->mSession.mState != SessionState_Locked)
13032 return VBOX_E_INVALID_OBJECT_STATE;
13033
13034 /* Finalize the LaunchVMProcess progress object. */
13035 if (mData->mSession.mProgress)
13036 {
13037 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13038 mData->mSession.mProgress.setNull();
13039 }
13040
13041 if (SUCCEEDED((HRESULT)aResult))
13042 {
13043#ifdef VBOX_WITH_RESOURCE_USAGE_API
13044 /* The VM has been powered up successfully, so it makes sense
13045 * now to offer the performance metrics for a running machine
13046 * object. Doing it earlier wouldn't be safe. */
13047 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13048 mData->mSession.mPID);
13049#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13050 }
13051
13052 return S_OK;
13053}
13054
13055/**
13056 * @note Locks this object for writing.
13057 */
13058HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13059{
13060 LogFlowThisFuncEnter();
13061
13062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13063
13064 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13065 E_FAIL);
13066
13067 /* create a progress object to track operation completion */
13068 ComObjPtr<Progress> pProgress;
13069 pProgress.createObject();
13070 pProgress->init(i_getVirtualBox(),
13071 static_cast<IMachine *>(this) /* aInitiator */,
13072 Bstr(tr("Stopping the virtual machine")).raw(),
13073 FALSE /* aCancelable */);
13074
13075 /* fill in the console task data */
13076 mConsoleTaskData.mLastState = mData->mMachineState;
13077 mConsoleTaskData.mProgress = pProgress;
13078
13079 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13080 i_setMachineState(MachineState_Stopping);
13081
13082 pProgress.queryInterfaceTo(aProgress.asOutParam());
13083
13084 return S_OK;
13085}
13086
13087/**
13088 * @note Locks this object for writing.
13089 */
13090HRESULT SessionMachine::endPoweringDown(LONG aResult,
13091 const com::Utf8Str &aErrMsg)
13092{
13093 LogFlowThisFuncEnter();
13094
13095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13096
13097 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13098 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13099 && mConsoleTaskData.mLastState != MachineState_Null,
13100 E_FAIL);
13101
13102 /*
13103 * On failure, set the state to the state we had when BeginPoweringDown()
13104 * was called (this is expected by Console::PowerDown() and the associated
13105 * task). On success the VM process already changed the state to
13106 * MachineState_PoweredOff, so no need to do anything.
13107 */
13108 if (FAILED(aResult))
13109 i_setMachineState(mConsoleTaskData.mLastState);
13110
13111 /* notify the progress object about operation completion */
13112 Assert(mConsoleTaskData.mProgress);
13113 if (SUCCEEDED(aResult))
13114 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13115 else
13116 {
13117 if (aErrMsg.length())
13118 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13119 COM_IIDOF(ISession),
13120 getComponentName(),
13121 aErrMsg.c_str());
13122 else
13123 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13124 }
13125
13126 /* clear out the temporary saved state data */
13127 mConsoleTaskData.mLastState = MachineState_Null;
13128 mConsoleTaskData.mProgress.setNull();
13129
13130 LogFlowThisFuncLeave();
13131 return S_OK;
13132}
13133
13134
13135/**
13136 * Goes through the USB filters of the given machine to see if the given
13137 * device matches any filter or not.
13138 *
13139 * @note Locks the same as USBController::hasMatchingFilter() does.
13140 */
13141HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13142 BOOL *aMatched,
13143 ULONG *aMaskedInterfaces)
13144{
13145 LogFlowThisFunc(("\n"));
13146
13147#ifdef VBOX_WITH_USB
13148 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13149#else
13150 NOREF(aDevice);
13151 NOREF(aMaskedInterfaces);
13152 *aMatched = FALSE;
13153#endif
13154
13155 return S_OK;
13156}
13157
13158/**
13159 * @note Locks the same as Host::captureUSBDevice() does.
13160 */
13161HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13162{
13163 LogFlowThisFunc(("\n"));
13164
13165#ifdef VBOX_WITH_USB
13166 /* if captureDeviceForVM() fails, it must have set extended error info */
13167 clearError();
13168 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13169 if (FAILED(rc)) return rc;
13170
13171 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13172 AssertReturn(service, E_FAIL);
13173 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13174#else
13175 NOREF(aId);
13176 return E_NOTIMPL;
13177#endif
13178}
13179
13180/**
13181 * @note Locks the same as Host::detachUSBDevice() does.
13182 */
13183HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13184 BOOL aDone)
13185{
13186 LogFlowThisFunc(("\n"));
13187
13188#ifdef VBOX_WITH_USB
13189 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13190 AssertReturn(service, E_FAIL);
13191 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13192#else
13193 NOREF(aId);
13194 NOREF(aDone);
13195 return E_NOTIMPL;
13196#endif
13197}
13198
13199/**
13200 * Inserts all machine filters to the USB proxy service and then calls
13201 * Host::autoCaptureUSBDevices().
13202 *
13203 * Called by Console from the VM process upon VM startup.
13204 *
13205 * @note Locks what called methods lock.
13206 */
13207HRESULT SessionMachine::autoCaptureUSBDevices()
13208{
13209 LogFlowThisFunc(("\n"));
13210
13211#ifdef VBOX_WITH_USB
13212 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13213 AssertComRC(rc);
13214 NOREF(rc);
13215
13216 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13217 AssertReturn(service, E_FAIL);
13218 return service->autoCaptureDevicesForVM(this);
13219#else
13220 return S_OK;
13221#endif
13222}
13223
13224/**
13225 * Removes all machine filters from the USB proxy service and then calls
13226 * Host::detachAllUSBDevices().
13227 *
13228 * Called by Console from the VM process upon normal VM termination or by
13229 * SessionMachine::uninit() upon abnormal VM termination (from under the
13230 * Machine/SessionMachine lock).
13231 *
13232 * @note Locks what called methods lock.
13233 */
13234HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13235{
13236 LogFlowThisFunc(("\n"));
13237
13238#ifdef VBOX_WITH_USB
13239 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13240 AssertComRC(rc);
13241 NOREF(rc);
13242
13243 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13244 AssertReturn(service, E_FAIL);
13245 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13246#else
13247 NOREF(aDone);
13248 return S_OK;
13249#endif
13250}
13251
13252/**
13253 * @note Locks this object for writing.
13254 */
13255HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13256 ComPtr<IProgress> &aProgress)
13257{
13258 LogFlowThisFuncEnter();
13259
13260 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13261 /*
13262 * We don't assert below because it might happen that a non-direct session
13263 * informs us it is closed right after we've been uninitialized -- it's ok.
13264 */
13265
13266 /* get IInternalSessionControl interface */
13267 ComPtr<IInternalSessionControl> control(aSession);
13268
13269 ComAssertRet(!control.isNull(), E_INVALIDARG);
13270
13271 /* Creating a Progress object requires the VirtualBox lock, and
13272 * thus locking it here is required by the lock order rules. */
13273 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13274
13275 if (control == mData->mSession.mDirectControl)
13276 {
13277 /* The direct session is being normally closed by the client process
13278 * ----------------------------------------------------------------- */
13279
13280 /* go to the closing state (essential for all open*Session() calls and
13281 * for #checkForDeath()) */
13282 Assert(mData->mSession.mState == SessionState_Locked);
13283 mData->mSession.mState = SessionState_Unlocking;
13284
13285 /* set direct control to NULL to release the remote instance */
13286 mData->mSession.mDirectControl.setNull();
13287 LogFlowThisFunc(("Direct control is set to NULL\n"));
13288
13289 if (mData->mSession.mProgress)
13290 {
13291 /* finalize the progress, someone might wait if a frontend
13292 * closes the session before powering on the VM. */
13293 mData->mSession.mProgress->notifyComplete(E_FAIL,
13294 COM_IIDOF(ISession),
13295 getComponentName(),
13296 tr("The VM session was closed before any attempt to power it on"));
13297 mData->mSession.mProgress.setNull();
13298 }
13299
13300 /* Create the progress object the client will use to wait until
13301 * #checkForDeath() is called to uninitialize this session object after
13302 * it releases the IPC semaphore.
13303 * Note! Because we're "reusing" mProgress here, this must be a proxy
13304 * object just like for LaunchVMProcess. */
13305 Assert(mData->mSession.mProgress.isNull());
13306 ComObjPtr<ProgressProxy> progress;
13307 progress.createObject();
13308 ComPtr<IUnknown> pPeer(mPeer);
13309 progress->init(mParent, pPeer,
13310 Bstr(tr("Closing session")).raw(),
13311 FALSE /* aCancelable */);
13312 progress.queryInterfaceTo(aProgress.asOutParam());
13313 mData->mSession.mProgress = progress;
13314 }
13315 else
13316 {
13317 /* the remote session is being normally closed */
13318 Data::Session::RemoteControlList::iterator it =
13319 mData->mSession.mRemoteControls.begin();
13320 while (it != mData->mSession.mRemoteControls.end())
13321 {
13322 if (control == *it)
13323 break;
13324 ++it;
13325 }
13326 BOOL found = it != mData->mSession.mRemoteControls.end();
13327 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13328 E_INVALIDARG);
13329 // This MUST be erase(it), not remove(*it) as the latter triggers a
13330 // very nasty use after free due to the place where the value "lives".
13331 mData->mSession.mRemoteControls.erase(it);
13332 }
13333
13334 /* signal the client watcher thread, because the client is going away */
13335 mParent->i_updateClientWatcher();
13336
13337 LogFlowThisFuncLeave();
13338 return S_OK;
13339}
13340
13341HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13342 std::vector<com::Utf8Str> &aValues,
13343 std::vector<LONG64> &aTimestamps,
13344 std::vector<com::Utf8Str> &aFlags)
13345{
13346 LogFlowThisFunc(("\n"));
13347
13348#ifdef VBOX_WITH_GUEST_PROPS
13349 using namespace guestProp;
13350
13351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13352
13353 size_t cEntries = mHWData->mGuestProperties.size();
13354 aNames.resize(cEntries);
13355 aValues.resize(cEntries);
13356 aTimestamps.resize(cEntries);
13357 aFlags.resize(cEntries);
13358
13359 size_t i = 0;
13360 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13361 it != mHWData->mGuestProperties.end();
13362 ++it, ++i)
13363 {
13364 char szFlags[MAX_FLAGS_LEN + 1];
13365 aNames[i] = it->first;
13366 aValues[i] = it->second.strValue;
13367 aTimestamps[i] = it->second.mTimestamp;
13368
13369 /* If it is NULL, keep it NULL. */
13370 if (it->second.mFlags)
13371 {
13372 writeFlags(it->second.mFlags, szFlags);
13373 aFlags[i] = szFlags;
13374 }
13375 else
13376 aFlags[i] = "";
13377 }
13378 return S_OK;
13379#else
13380 ReturnComNotImplemented();
13381#endif
13382}
13383
13384HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13385 const com::Utf8Str &aValue,
13386 LONG64 aTimestamp,
13387 const com::Utf8Str &aFlags)
13388{
13389 LogFlowThisFunc(("\n"));
13390
13391#ifdef VBOX_WITH_GUEST_PROPS
13392 using namespace guestProp;
13393
13394 try
13395 {
13396 /*
13397 * Convert input up front.
13398 */
13399 uint32_t fFlags = NILFLAG;
13400 if (aFlags.length())
13401 {
13402 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13403 AssertRCReturn(vrc, E_INVALIDARG);
13404 }
13405
13406 /*
13407 * Now grab the object lock, validate the state and do the update.
13408 */
13409
13410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13411
13412 if (!Global::IsOnline(mData->mMachineState))
13413 {
13414 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13415 VBOX_E_INVALID_VM_STATE);
13416 }
13417
13418 i_setModified(IsModified_MachineData);
13419 mHWData.backup();
13420
13421 bool fDelete = !aValue.length();
13422 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13423 if (it != mHWData->mGuestProperties.end())
13424 {
13425 if (!fDelete)
13426 {
13427 it->second.strValue = aValue;
13428 it->second.mTimestamp = aTimestamp;
13429 it->second.mFlags = fFlags;
13430 }
13431 else
13432 mHWData->mGuestProperties.erase(it);
13433
13434 mData->mGuestPropertiesModified = TRUE;
13435 }
13436 else if (!fDelete)
13437 {
13438 HWData::GuestProperty prop;
13439 prop.strValue = aValue;
13440 prop.mTimestamp = aTimestamp;
13441 prop.mFlags = fFlags;
13442
13443 mHWData->mGuestProperties[aName] = prop;
13444 mData->mGuestPropertiesModified = TRUE;
13445 }
13446
13447 alock.release();
13448
13449 mParent->i_onGuestPropertyChange(mData->mUuid,
13450 Bstr(aName).raw(),
13451 Bstr(aValue).raw(),
13452 Bstr(aFlags).raw());
13453 }
13454 catch (...)
13455 {
13456 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13457 }
13458 return S_OK;
13459#else
13460 ReturnComNotImplemented();
13461#endif
13462}
13463
13464
13465HRESULT SessionMachine::lockMedia()
13466{
13467 AutoMultiWriteLock2 alock(this->lockHandle(),
13468 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13469
13470 AssertReturn( mData->mMachineState == MachineState_Starting
13471 || mData->mMachineState == MachineState_Restoring
13472 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13473
13474 clearError();
13475 alock.release();
13476 return i_lockMedia();
13477}
13478
13479HRESULT SessionMachine::unlockMedia()
13480{
13481 HRESULT hrc = i_unlockMedia();
13482 return hrc;
13483}
13484
13485HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13486 ComPtr<IMediumAttachment> &aNewAttachment)
13487{
13488 // request the host lock first, since might be calling Host methods for getting host drives;
13489 // next, protect the media tree all the while we're in here, as well as our member variables
13490 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13491 this->lockHandle(),
13492 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13493
13494 IMediumAttachment *iAttach = aAttachment;
13495 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13496
13497 Bstr ctrlName;
13498 LONG lPort;
13499 LONG lDevice;
13500 bool fTempEject;
13501 {
13502 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13503
13504 /* Need to query the details first, as the IMediumAttachment reference
13505 * might be to the original settings, which we are going to change. */
13506 ctrlName = pAttach->i_getControllerName();
13507 lPort = pAttach->i_getPort();
13508 lDevice = pAttach->i_getDevice();
13509 fTempEject = pAttach->i_getTempEject();
13510 }
13511
13512 if (!fTempEject)
13513 {
13514 /* Remember previously mounted medium. The medium before taking the
13515 * backup is not necessarily the same thing. */
13516 ComObjPtr<Medium> oldmedium;
13517 oldmedium = pAttach->i_getMedium();
13518
13519 i_setModified(IsModified_Storage);
13520 mMediaData.backup();
13521
13522 // The backup operation makes the pAttach reference point to the
13523 // old settings. Re-get the correct reference.
13524 pAttach = i_findAttachment(mMediaData->mAttachments,
13525 ctrlName.raw(),
13526 lPort,
13527 lDevice);
13528
13529 {
13530 AutoCaller autoAttachCaller(this);
13531 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13532
13533 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13534 if (!oldmedium.isNull())
13535 oldmedium->i_removeBackReference(mData->mUuid);
13536
13537 pAttach->i_updateMedium(NULL);
13538 pAttach->i_updateEjected();
13539 }
13540
13541 i_setModified(IsModified_Storage);
13542 }
13543 else
13544 {
13545 {
13546 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13547 pAttach->i_updateEjected();
13548 }
13549 }
13550
13551 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13552
13553 return S_OK;
13554}
13555
13556HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13557 com::Utf8Str &aResult)
13558{
13559 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13560
13561 HRESULT hr = S_OK;
13562
13563 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13564 {
13565 enum VRDEAuthParams
13566 {
13567 parmUuid = 1,
13568 parmGuestJudgement,
13569 parmUser,
13570 parmPassword,
13571 parmDomain,
13572 parmClientId
13573 };
13574
13575 AuthResult result = AuthResultAccessDenied;
13576
13577 if (!mAuthLibCtx.hAuthLibrary)
13578 {
13579 /* Load the external authentication library. */
13580 Bstr authLibrary;
13581 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13582
13583 Utf8Str filename = authLibrary;
13584
13585 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13586 if (RT_FAILURE(rc))
13587 {
13588 hr = setError(E_FAIL,
13589 tr("Could not load the external authentication library '%s' (%Rrc)"),
13590 filename.c_str(), rc);
13591 }
13592 }
13593
13594 if (SUCCEEDED(hr))
13595 {
13596 Guid uuid(aAuthParams[parmUuid]);
13597 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13598 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13599
13600 result = AuthLibAuthenticate(&mAuthLibCtx,
13601 uuid.raw(), guestJudgement,
13602 aAuthParams[parmUser].c_str(),
13603 aAuthParams[parmPassword].c_str(),
13604 aAuthParams[parmDomain].c_str(),
13605 u32ClientId);
13606 }
13607
13608 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13609 size_t cbPassword = aAuthParams[parmPassword].length();
13610 if (cbPassword)
13611 {
13612 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13613 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13614 }
13615
13616 if (result == AuthResultAccessGranted)
13617 aResult = "granted";
13618 else
13619 aResult = "denied";
13620
13621 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13622 aAuthParams[parmUser].c_str(), aResult.c_str()));
13623 }
13624 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13625 {
13626 enum VRDEAuthDisconnectParams
13627 {
13628 parmUuid = 1,
13629 parmClientId
13630 };
13631
13632 Guid uuid(aAuthParams[parmUuid]);
13633 uint32_t u32ClientId = 0;
13634 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13635 }
13636 else
13637 {
13638 hr = E_INVALIDARG;
13639 }
13640
13641 return hr;
13642}
13643
13644// public methods only for internal purposes
13645/////////////////////////////////////////////////////////////////////////////
13646
13647#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13648/**
13649 * Called from the client watcher thread to check for expected or unexpected
13650 * death of the client process that has a direct session to this machine.
13651 *
13652 * On Win32 and on OS/2, this method is called only when we've got the
13653 * mutex (i.e. the client has either died or terminated normally) so it always
13654 * returns @c true (the client is terminated, the session machine is
13655 * uninitialized).
13656 *
13657 * On other platforms, the method returns @c true if the client process has
13658 * terminated normally or abnormally and the session machine was uninitialized,
13659 * and @c false if the client process is still alive.
13660 *
13661 * @note Locks this object for writing.
13662 */
13663bool SessionMachine::i_checkForDeath()
13664{
13665 Uninit::Reason reason;
13666 bool terminated = false;
13667
13668 /* Enclose autoCaller with a block because calling uninit() from under it
13669 * will deadlock. */
13670 {
13671 AutoCaller autoCaller(this);
13672 if (!autoCaller.isOk())
13673 {
13674 /* return true if not ready, to cause the client watcher to exclude
13675 * the corresponding session from watching */
13676 LogFlowThisFunc(("Already uninitialized!\n"));
13677 return true;
13678 }
13679
13680 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13681
13682 /* Determine the reason of death: if the session state is Closing here,
13683 * everything is fine. Otherwise it means that the client did not call
13684 * OnSessionEnd() before it released the IPC semaphore. This may happen
13685 * either because the client process has abnormally terminated, or
13686 * because it simply forgot to call ISession::Close() before exiting. We
13687 * threat the latter also as an abnormal termination (see
13688 * Session::uninit() for details). */
13689 reason = mData->mSession.mState == SessionState_Unlocking ?
13690 Uninit::Normal :
13691 Uninit::Abnormal;
13692
13693 if (mClientToken)
13694 terminated = mClientToken->release();
13695 } /* AutoCaller block */
13696
13697 if (terminated)
13698 uninit(reason);
13699
13700 return terminated;
13701}
13702
13703void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13704{
13705 LogFlowThisFunc(("\n"));
13706
13707 strTokenId.setNull();
13708
13709 AutoCaller autoCaller(this);
13710 AssertComRCReturnVoid(autoCaller.rc());
13711
13712 Assert(mClientToken);
13713 if (mClientToken)
13714 mClientToken->getId(strTokenId);
13715}
13716#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13717IToken *SessionMachine::i_getToken()
13718{
13719 LogFlowThisFunc(("\n"));
13720
13721 AutoCaller autoCaller(this);
13722 AssertComRCReturn(autoCaller.rc(), NULL);
13723
13724 Assert(mClientToken);
13725 if (mClientToken)
13726 return mClientToken->getToken();
13727 else
13728 return NULL;
13729}
13730#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13731
13732Machine::ClientToken *SessionMachine::i_getClientToken()
13733{
13734 LogFlowThisFunc(("\n"));
13735
13736 AutoCaller autoCaller(this);
13737 AssertComRCReturn(autoCaller.rc(), NULL);
13738
13739 return mClientToken;
13740}
13741
13742
13743/**
13744 * @note Locks this object for reading.
13745 */
13746HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13747{
13748 LogFlowThisFunc(("\n"));
13749
13750 AutoCaller autoCaller(this);
13751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13752
13753 ComPtr<IInternalSessionControl> directControl;
13754 {
13755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13756 if (mData->mSession.mLockType == LockType_VM)
13757 directControl = mData->mSession.mDirectControl;
13758 }
13759
13760 /* ignore notifications sent after #OnSessionEnd() is called */
13761 if (!directControl)
13762 return S_OK;
13763
13764 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13765}
13766
13767/**
13768 * @note Locks this object for reading.
13769 */
13770HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13771 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13772 IN_BSTR aGuestIp, LONG aGuestPort)
13773{
13774 LogFlowThisFunc(("\n"));
13775
13776 AutoCaller autoCaller(this);
13777 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13778
13779 ComPtr<IInternalSessionControl> directControl;
13780 {
13781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13782 if (mData->mSession.mLockType == LockType_VM)
13783 directControl = mData->mSession.mDirectControl;
13784 }
13785
13786 /* ignore notifications sent after #OnSessionEnd() is called */
13787 if (!directControl)
13788 return S_OK;
13789 /*
13790 * instead acting like callback we ask IVirtualBox deliver corresponding event
13791 */
13792
13793 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13794 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13795 return S_OK;
13796}
13797
13798/**
13799 * @note Locks this object for reading.
13800 */
13801HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13802{
13803 LogFlowThisFunc(("\n"));
13804
13805 AutoCaller autoCaller(this);
13806 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13807
13808 ComPtr<IInternalSessionControl> directControl;
13809 {
13810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13811 if (mData->mSession.mLockType == LockType_VM)
13812 directControl = mData->mSession.mDirectControl;
13813 }
13814
13815 /* ignore notifications sent after #OnSessionEnd() is called */
13816 if (!directControl)
13817 return S_OK;
13818
13819 return directControl->OnSerialPortChange(serialPort);
13820}
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13826{
13827 LogFlowThisFunc(("\n"));
13828
13829 AutoCaller autoCaller(this);
13830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13831
13832 ComPtr<IInternalSessionControl> directControl;
13833 {
13834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13835 if (mData->mSession.mLockType == LockType_VM)
13836 directControl = mData->mSession.mDirectControl;
13837 }
13838
13839 /* ignore notifications sent after #OnSessionEnd() is called */
13840 if (!directControl)
13841 return S_OK;
13842
13843 return directControl->OnParallelPortChange(parallelPort);
13844}
13845
13846/**
13847 * @note Locks this object for reading.
13848 */
13849HRESULT SessionMachine::i_onStorageControllerChange()
13850{
13851 LogFlowThisFunc(("\n"));
13852
13853 AutoCaller autoCaller(this);
13854 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13855
13856 ComPtr<IInternalSessionControl> directControl;
13857 {
13858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13859 if (mData->mSession.mLockType == LockType_VM)
13860 directControl = mData->mSession.mDirectControl;
13861 }
13862
13863 /* ignore notifications sent after #OnSessionEnd() is called */
13864 if (!directControl)
13865 return S_OK;
13866
13867 return directControl->OnStorageControllerChange();
13868}
13869
13870/**
13871 * @note Locks this object for reading.
13872 */
13873HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13874{
13875 LogFlowThisFunc(("\n"));
13876
13877 AutoCaller autoCaller(this);
13878 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13879
13880 ComPtr<IInternalSessionControl> directControl;
13881 {
13882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13883 if (mData->mSession.mLockType == LockType_VM)
13884 directControl = mData->mSession.mDirectControl;
13885 }
13886
13887 /* ignore notifications sent after #OnSessionEnd() is called */
13888 if (!directControl)
13889 return S_OK;
13890
13891 return directControl->OnMediumChange(aAttachment, aForce);
13892}
13893
13894/**
13895 * @note Locks this object for reading.
13896 */
13897HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13898{
13899 LogFlowThisFunc(("\n"));
13900
13901 AutoCaller autoCaller(this);
13902 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13903
13904 ComPtr<IInternalSessionControl> directControl;
13905 {
13906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13907 if (mData->mSession.mLockType == LockType_VM)
13908 directControl = mData->mSession.mDirectControl;
13909 }
13910
13911 /* ignore notifications sent after #OnSessionEnd() is called */
13912 if (!directControl)
13913 return S_OK;
13914
13915 return directControl->OnCPUChange(aCPU, aRemove);
13916}
13917
13918HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13919{
13920 LogFlowThisFunc(("\n"));
13921
13922 AutoCaller autoCaller(this);
13923 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13924
13925 ComPtr<IInternalSessionControl> directControl;
13926 {
13927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13928 if (mData->mSession.mLockType == LockType_VM)
13929 directControl = mData->mSession.mDirectControl;
13930 }
13931
13932 /* ignore notifications sent after #OnSessionEnd() is called */
13933 if (!directControl)
13934 return S_OK;
13935
13936 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13937}
13938
13939/**
13940 * @note Locks this object for reading.
13941 */
13942HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13943{
13944 LogFlowThisFunc(("\n"));
13945
13946 AutoCaller autoCaller(this);
13947 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13948
13949 ComPtr<IInternalSessionControl> directControl;
13950 {
13951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13952 if (mData->mSession.mLockType == LockType_VM)
13953 directControl = mData->mSession.mDirectControl;
13954 }
13955
13956 /* ignore notifications sent after #OnSessionEnd() is called */
13957 if (!directControl)
13958 return S_OK;
13959
13960 return directControl->OnVRDEServerChange(aRestart);
13961}
13962
13963/**
13964 * @note Locks this object for reading.
13965 */
13966HRESULT SessionMachine::i_onVideoCaptureChange()
13967{
13968 LogFlowThisFunc(("\n"));
13969
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 if (mData->mSession.mLockType == LockType_VM)
13977 directControl = mData->mSession.mDirectControl;
13978 }
13979
13980 /* ignore notifications sent after #OnSessionEnd() is called */
13981 if (!directControl)
13982 return S_OK;
13983
13984 return directControl->OnVideoCaptureChange();
13985}
13986
13987/**
13988 * @note Locks this object for reading.
13989 */
13990HRESULT SessionMachine::i_onUSBControllerChange()
13991{
13992 LogFlowThisFunc(("\n"));
13993
13994 AutoCaller autoCaller(this);
13995 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13996
13997 ComPtr<IInternalSessionControl> directControl;
13998 {
13999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14000 if (mData->mSession.mLockType == LockType_VM)
14001 directControl = mData->mSession.mDirectControl;
14002 }
14003
14004 /* ignore notifications sent after #OnSessionEnd() is called */
14005 if (!directControl)
14006 return S_OK;
14007
14008 return directControl->OnUSBControllerChange();
14009}
14010
14011/**
14012 * @note Locks this object for reading.
14013 */
14014HRESULT SessionMachine::i_onSharedFolderChange()
14015{
14016 LogFlowThisFunc(("\n"));
14017
14018 AutoCaller autoCaller(this);
14019 AssertComRCReturnRC(autoCaller.rc());
14020
14021 ComPtr<IInternalSessionControl> directControl;
14022 {
14023 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14024 if (mData->mSession.mLockType == LockType_VM)
14025 directControl = mData->mSession.mDirectControl;
14026 }
14027
14028 /* ignore notifications sent after #OnSessionEnd() is called */
14029 if (!directControl)
14030 return S_OK;
14031
14032 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14033}
14034
14035/**
14036 * @note Locks this object for reading.
14037 */
14038HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14039{
14040 LogFlowThisFunc(("\n"));
14041
14042 AutoCaller autoCaller(this);
14043 AssertComRCReturnRC(autoCaller.rc());
14044
14045 ComPtr<IInternalSessionControl> directControl;
14046 {
14047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14048 if (mData->mSession.mLockType == LockType_VM)
14049 directControl = mData->mSession.mDirectControl;
14050 }
14051
14052 /* ignore notifications sent after #OnSessionEnd() is called */
14053 if (!directControl)
14054 return S_OK;
14055
14056 return directControl->OnClipboardModeChange(aClipboardMode);
14057}
14058
14059/**
14060 * @note Locks this object for reading.
14061 */
14062HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14063{
14064 LogFlowThisFunc(("\n"));
14065
14066 AutoCaller autoCaller(this);
14067 AssertComRCReturnRC(autoCaller.rc());
14068
14069 ComPtr<IInternalSessionControl> directControl;
14070 {
14071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14072 if (mData->mSession.mLockType == LockType_VM)
14073 directControl = mData->mSession.mDirectControl;
14074 }
14075
14076 /* ignore notifications sent after #OnSessionEnd() is called */
14077 if (!directControl)
14078 return S_OK;
14079
14080 return directControl->OnDnDModeChange(aDnDMode);
14081}
14082
14083/**
14084 * @note Locks this object for reading.
14085 */
14086HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14087{
14088 LogFlowThisFunc(("\n"));
14089
14090 AutoCaller autoCaller(this);
14091 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14092
14093 ComPtr<IInternalSessionControl> directControl;
14094 {
14095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14096 if (mData->mSession.mLockType == LockType_VM)
14097 directControl = mData->mSession.mDirectControl;
14098 }
14099
14100 /* ignore notifications sent after #OnSessionEnd() is called */
14101 if (!directControl)
14102 return S_OK;
14103
14104 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14105}
14106
14107/**
14108 * @note Locks this object for reading.
14109 */
14110HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14111{
14112 LogFlowThisFunc(("\n"));
14113
14114 AutoCaller autoCaller(this);
14115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14116
14117 ComPtr<IInternalSessionControl> directControl;
14118 {
14119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14120 if (mData->mSession.mLockType == LockType_VM)
14121 directControl = mData->mSession.mDirectControl;
14122 }
14123
14124 /* ignore notifications sent after #OnSessionEnd() is called */
14125 if (!directControl)
14126 return S_OK;
14127
14128 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14129}
14130
14131/**
14132 * Returns @c true if this machine's USB controller reports it has a matching
14133 * filter for the given USB device and @c false otherwise.
14134 *
14135 * @note locks this object for reading.
14136 */
14137bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14138{
14139 AutoCaller autoCaller(this);
14140 /* silently return if not ready -- this method may be called after the
14141 * direct machine session has been called */
14142 if (!autoCaller.isOk())
14143 return false;
14144
14145#ifdef VBOX_WITH_USB
14146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14147
14148 switch (mData->mMachineState)
14149 {
14150 case MachineState_Starting:
14151 case MachineState_Restoring:
14152 case MachineState_TeleportingIn:
14153 case MachineState_Paused:
14154 case MachineState_Running:
14155 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14156 * elsewhere... */
14157 alock.release();
14158 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14159 default: break;
14160 }
14161#else
14162 NOREF(aDevice);
14163 NOREF(aMaskedIfs);
14164#endif
14165 return false;
14166}
14167
14168/**
14169 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14170 */
14171HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14172 IVirtualBoxErrorInfo *aError,
14173 ULONG aMaskedIfs,
14174 const com::Utf8Str &aCaptureFilename)
14175{
14176 LogFlowThisFunc(("\n"));
14177
14178 AutoCaller autoCaller(this);
14179
14180 /* This notification may happen after the machine object has been
14181 * uninitialized (the session was closed), so don't assert. */
14182 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14183
14184 ComPtr<IInternalSessionControl> directControl;
14185 {
14186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14187 if (mData->mSession.mLockType == LockType_VM)
14188 directControl = mData->mSession.mDirectControl;
14189 }
14190
14191 /* fail on notifications sent after #OnSessionEnd() is called, it is
14192 * expected by the caller */
14193 if (!directControl)
14194 return E_FAIL;
14195
14196 /* No locks should be held at this point. */
14197 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14198 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14199
14200 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14201}
14202
14203/**
14204 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14205 */
14206HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14207 IVirtualBoxErrorInfo *aError)
14208{
14209 LogFlowThisFunc(("\n"));
14210
14211 AutoCaller autoCaller(this);
14212
14213 /* This notification may happen after the machine object has been
14214 * uninitialized (the session was closed), so don't assert. */
14215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* fail on notifications sent after #OnSessionEnd() is called, it is
14225 * expected by the caller */
14226 if (!directControl)
14227 return E_FAIL;
14228
14229 /* No locks should be held at this point. */
14230 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14231 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14232
14233 return directControl->OnUSBDeviceDetach(aId, aError);
14234}
14235
14236// protected methods
14237/////////////////////////////////////////////////////////////////////////////
14238
14239/**
14240 * Deletes the given file if it is no longer in use by either the current machine state
14241 * (if the machine is "saved") or any of the machine's snapshots.
14242 *
14243 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14244 * but is different for each SnapshotMachine. When calling this, the order of calling this
14245 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14246 * is therefore critical. I know, it's all rather messy.
14247 *
14248 * @param strStateFile
14249 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14250 * the test for whether the saved state file is in use.
14251 */
14252void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14253 Snapshot *pSnapshotToIgnore)
14254{
14255 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14256 if ( (strStateFile.isNotEmpty())
14257 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14258 )
14259 // ... and it must also not be shared with other snapshots
14260 if ( !mData->mFirstSnapshot
14261 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14262 // this checks the SnapshotMachine's state file paths
14263 )
14264 RTFileDelete(strStateFile.c_str());
14265}
14266
14267/**
14268 * Locks the attached media.
14269 *
14270 * All attached hard disks are locked for writing and DVD/floppy are locked for
14271 * reading. Parents of attached hard disks (if any) are locked for reading.
14272 *
14273 * This method also performs accessibility check of all media it locks: if some
14274 * media is inaccessible, the method will return a failure and a bunch of
14275 * extended error info objects per each inaccessible medium.
14276 *
14277 * Note that this method is atomic: if it returns a success, all media are
14278 * locked as described above; on failure no media is locked at all (all
14279 * succeeded individual locks will be undone).
14280 *
14281 * The caller is responsible for doing the necessary state sanity checks.
14282 *
14283 * The locks made by this method must be undone by calling #unlockMedia() when
14284 * no more needed.
14285 */
14286HRESULT SessionMachine::i_lockMedia()
14287{
14288 AutoCaller autoCaller(this);
14289 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14290
14291 AutoMultiWriteLock2 alock(this->lockHandle(),
14292 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14293
14294 /* bail out if trying to lock things with already set up locking */
14295 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14296
14297 MultiResult mrc(S_OK);
14298
14299 /* Collect locking information for all medium objects attached to the VM. */
14300 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14301 it != mMediaData->mAttachments.end();
14302 ++it)
14303 {
14304 MediumAttachment* pAtt = *it;
14305 DeviceType_T devType = pAtt->i_getType();
14306 Medium *pMedium = pAtt->i_getMedium();
14307
14308 MediumLockList *pMediumLockList(new MediumLockList());
14309 // There can be attachments without a medium (floppy/dvd), and thus
14310 // it's impossible to create a medium lock list. It still makes sense
14311 // to have the empty medium lock list in the map in case a medium is
14312 // attached later.
14313 if (pMedium != NULL)
14314 {
14315 MediumType_T mediumType = pMedium->i_getType();
14316 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14317 || mediumType == MediumType_Shareable;
14318 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14319
14320 alock.release();
14321 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14322 !fIsReadOnlyLock /* fMediumLockWrite */,
14323 false /* fMediumLockWriteAll */,
14324 NULL,
14325 *pMediumLockList);
14326 alock.acquire();
14327 if (FAILED(mrc))
14328 {
14329 delete pMediumLockList;
14330 mData->mSession.mLockedMedia.Clear();
14331 break;
14332 }
14333 }
14334
14335 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14336 if (FAILED(rc))
14337 {
14338 mData->mSession.mLockedMedia.Clear();
14339 mrc = setError(rc,
14340 tr("Collecting locking information for all attached media failed"));
14341 break;
14342 }
14343 }
14344
14345 if (SUCCEEDED(mrc))
14346 {
14347 /* Now lock all media. If this fails, nothing is locked. */
14348 alock.release();
14349 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14350 alock.acquire();
14351 if (FAILED(rc))
14352 {
14353 mrc = setError(rc,
14354 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14355 }
14356 }
14357
14358 return mrc;
14359}
14360
14361/**
14362 * Undoes the locks made by by #lockMedia().
14363 */
14364HRESULT SessionMachine::i_unlockMedia()
14365{
14366 AutoCaller autoCaller(this);
14367 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14368
14369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14370
14371 /* we may be holding important error info on the current thread;
14372 * preserve it */
14373 ErrorInfoKeeper eik;
14374
14375 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14376 AssertComRC(rc);
14377 return rc;
14378}
14379
14380/**
14381 * Helper to change the machine state (reimplementation).
14382 *
14383 * @note Locks this object for writing.
14384 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14385 * it can cause crashes in random places due to unexpectedly committing
14386 * the current settings. The caller is responsible for that. The call
14387 * to saveStateSettings is fine, because this method does not commit.
14388 */
14389HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14390{
14391 LogFlowThisFuncEnter();
14392 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14393
14394 AutoCaller autoCaller(this);
14395 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14396
14397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14398
14399 MachineState_T oldMachineState = mData->mMachineState;
14400
14401 AssertMsgReturn(oldMachineState != aMachineState,
14402 ("oldMachineState=%s, aMachineState=%s\n",
14403 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14404 E_FAIL);
14405
14406 HRESULT rc = S_OK;
14407
14408 int stsFlags = 0;
14409 bool deleteSavedState = false;
14410
14411 /* detect some state transitions */
14412
14413 if ( ( oldMachineState == MachineState_Saved
14414 && aMachineState == MachineState_Restoring)
14415 || ( ( oldMachineState == MachineState_PoweredOff
14416 || oldMachineState == MachineState_Teleported
14417 || oldMachineState == MachineState_Aborted
14418 )
14419 && ( aMachineState == MachineState_TeleportingIn
14420 || aMachineState == MachineState_Starting
14421 )
14422 )
14423 )
14424 {
14425 /* The EMT thread is about to start */
14426
14427 /* Nothing to do here for now... */
14428
14429 /// @todo NEWMEDIA don't let mDVDDrive and other children
14430 /// change anything when in the Starting/Restoring state
14431 }
14432 else if ( ( oldMachineState == MachineState_Running
14433 || oldMachineState == MachineState_Paused
14434 || oldMachineState == MachineState_Teleporting
14435 || oldMachineState == MachineState_OnlineSnapshotting
14436 || oldMachineState == MachineState_LiveSnapshotting
14437 || oldMachineState == MachineState_Stuck
14438 || oldMachineState == MachineState_Starting
14439 || oldMachineState == MachineState_Stopping
14440 || oldMachineState == MachineState_Saving
14441 || oldMachineState == MachineState_Restoring
14442 || oldMachineState == MachineState_TeleportingPausedVM
14443 || oldMachineState == MachineState_TeleportingIn
14444 )
14445 && ( aMachineState == MachineState_PoweredOff
14446 || aMachineState == MachineState_Saved
14447 || aMachineState == MachineState_Teleported
14448 || aMachineState == MachineState_Aborted
14449 )
14450 )
14451 {
14452 /* The EMT thread has just stopped, unlock attached media. Note that as
14453 * opposed to locking that is done from Console, we do unlocking here
14454 * because the VM process may have aborted before having a chance to
14455 * properly unlock all media it locked. */
14456
14457 unlockMedia();
14458 }
14459
14460 if (oldMachineState == MachineState_Restoring)
14461 {
14462 if (aMachineState != MachineState_Saved)
14463 {
14464 /*
14465 * delete the saved state file once the machine has finished
14466 * restoring from it (note that Console sets the state from
14467 * Restoring to Saved if the VM couldn't restore successfully,
14468 * to give the user an ability to fix an error and retry --
14469 * we keep the saved state file in this case)
14470 */
14471 deleteSavedState = true;
14472 }
14473 }
14474 else if ( oldMachineState == MachineState_Saved
14475 && ( aMachineState == MachineState_PoweredOff
14476 || aMachineState == MachineState_Aborted
14477 || aMachineState == MachineState_Teleported
14478 )
14479 )
14480 {
14481 /*
14482 * delete the saved state after SessionMachine::ForgetSavedState() is called
14483 * or if the VM process (owning a direct VM session) crashed while the
14484 * VM was Saved
14485 */
14486
14487 /// @todo (dmik)
14488 // Not sure that deleting the saved state file just because of the
14489 // client death before it attempted to restore the VM is a good
14490 // thing. But when it crashes we need to go to the Aborted state
14491 // which cannot have the saved state file associated... The only
14492 // way to fix this is to make the Aborted condition not a VM state
14493 // but a bool flag: i.e., when a crash occurs, set it to true and
14494 // change the state to PoweredOff or Saved depending on the
14495 // saved state presence.
14496
14497 deleteSavedState = true;
14498 mData->mCurrentStateModified = TRUE;
14499 stsFlags |= SaveSTS_CurStateModified;
14500 }
14501
14502 if ( aMachineState == MachineState_Starting
14503 || aMachineState == MachineState_Restoring
14504 || aMachineState == MachineState_TeleportingIn
14505 )
14506 {
14507 /* set the current state modified flag to indicate that the current
14508 * state is no more identical to the state in the
14509 * current snapshot */
14510 if (!mData->mCurrentSnapshot.isNull())
14511 {
14512 mData->mCurrentStateModified = TRUE;
14513 stsFlags |= SaveSTS_CurStateModified;
14514 }
14515 }
14516
14517 if (deleteSavedState)
14518 {
14519 if (mRemoveSavedState)
14520 {
14521 Assert(!mSSData->strStateFilePath.isEmpty());
14522
14523 // it is safe to delete the saved state file if ...
14524 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14525 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14526 // ... none of the snapshots share the saved state file
14527 )
14528 RTFileDelete(mSSData->strStateFilePath.c_str());
14529 }
14530
14531 mSSData->strStateFilePath.setNull();
14532 stsFlags |= SaveSTS_StateFilePath;
14533 }
14534
14535 /* redirect to the underlying peer machine */
14536 mPeer->i_setMachineState(aMachineState);
14537
14538 if ( oldMachineState != MachineState_RestoringSnapshot
14539 && ( aMachineState == MachineState_PoweredOff
14540 || aMachineState == MachineState_Teleported
14541 || aMachineState == MachineState_Aborted
14542 || aMachineState == MachineState_Saved))
14543 {
14544 /* the machine has stopped execution
14545 * (or the saved state file was adopted) */
14546 stsFlags |= SaveSTS_StateTimeStamp;
14547 }
14548
14549 if ( ( oldMachineState == MachineState_PoweredOff
14550 || oldMachineState == MachineState_Aborted
14551 || oldMachineState == MachineState_Teleported
14552 )
14553 && aMachineState == MachineState_Saved)
14554 {
14555 /* the saved state file was adopted */
14556 Assert(!mSSData->strStateFilePath.isEmpty());
14557 stsFlags |= SaveSTS_StateFilePath;
14558 }
14559
14560#ifdef VBOX_WITH_GUEST_PROPS
14561 if ( aMachineState == MachineState_PoweredOff
14562 || aMachineState == MachineState_Aborted
14563 || aMachineState == MachineState_Teleported)
14564 {
14565 /* Make sure any transient guest properties get removed from the
14566 * property store on shutdown. */
14567 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14568
14569 /* remove it from the settings representation */
14570 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14571 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14572 it != llGuestProperties.end();
14573 /*nothing*/)
14574 {
14575 const settings::GuestProperty &prop = *it;
14576 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14577 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14578 {
14579 it = llGuestProperties.erase(it);
14580 fNeedsSaving = true;
14581 }
14582 else
14583 {
14584 ++it;
14585 }
14586 }
14587
14588 /* Additionally remove it from the HWData representation. Required to
14589 * keep everything in sync, as this is what the API keeps using. */
14590 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14591 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14592 it != llHWGuestProperties.end();
14593 /*nothing*/)
14594 {
14595 uint32_t fFlags = it->second.mFlags;
14596 if ( fFlags & guestProp::TRANSIENT
14597 || fFlags & guestProp::TRANSRESET)
14598 {
14599 /* iterator where we need to continue after the erase call
14600 * (C++03 is a fact still, and it doesn't return the iterator
14601 * which would allow continuing) */
14602 HWData::GuestPropertyMap::iterator it2 = it;
14603 ++it2;
14604 llHWGuestProperties.erase(it);
14605 it = it2;
14606 fNeedsSaving = true;
14607 }
14608 else
14609 {
14610 ++it;
14611 }
14612 }
14613
14614 if (fNeedsSaving)
14615 {
14616 mData->mCurrentStateModified = TRUE;
14617 stsFlags |= SaveSTS_CurStateModified;
14618 }
14619 }
14620#endif /* VBOX_WITH_GUEST_PROPS */
14621
14622 rc = i_saveStateSettings(stsFlags);
14623
14624 if ( ( oldMachineState != MachineState_PoweredOff
14625 && oldMachineState != MachineState_Aborted
14626 && oldMachineState != MachineState_Teleported
14627 )
14628 && ( aMachineState == MachineState_PoweredOff
14629 || aMachineState == MachineState_Aborted
14630 || aMachineState == MachineState_Teleported
14631 )
14632 )
14633 {
14634 /* we've been shut down for any reason */
14635 /* no special action so far */
14636 }
14637
14638 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14639 LogFlowThisFuncLeave();
14640 return rc;
14641}
14642
14643/**
14644 * Sends the current machine state value to the VM process.
14645 *
14646 * @note Locks this object for reading, then calls a client process.
14647 */
14648HRESULT SessionMachine::i_updateMachineStateOnClient()
14649{
14650 AutoCaller autoCaller(this);
14651 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14652
14653 ComPtr<IInternalSessionControl> directControl;
14654 {
14655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14656 AssertReturn(!!mData, E_FAIL);
14657 if (mData->mSession.mLockType == LockType_VM)
14658 directControl = mData->mSession.mDirectControl;
14659
14660 /* directControl may be already set to NULL here in #OnSessionEnd()
14661 * called too early by the direct session process while there is still
14662 * some operation (like deleting the snapshot) in progress. The client
14663 * process in this case is waiting inside Session::close() for the
14664 * "end session" process object to complete, while #uninit() called by
14665 * #checkForDeath() on the Watcher thread is waiting for the pending
14666 * operation to complete. For now, we accept this inconsistent behavior
14667 * and simply do nothing here. */
14668
14669 if (mData->mSession.mState == SessionState_Unlocking)
14670 return S_OK;
14671 }
14672
14673 /* ignore notifications sent after #OnSessionEnd() is called */
14674 if (!directControl)
14675 return S_OK;
14676
14677 return directControl->UpdateMachineState(mData->mMachineState);
14678}
14679
14680
14681/**
14682 * Static Machine method that can get passed to RTThreadCreate to
14683 * have a thread started for a Task. See Machine::Task.
14684 */
14685/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14686{
14687 AssertReturn(pvUser, VERR_INVALID_POINTER);
14688
14689 Task *pTask = static_cast<Task *>(pvUser);
14690 pTask->handler();
14691 /** @todo r=klaus it would be safer to update the progress object here,
14692 * as it avoids possible races due to scoping issues/tricks in the handler */
14693 // it's our responsibility to delete the task
14694 delete pTask;
14695
14696 return 0;
14697}
14698
14699/*static*/
14700HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14701{
14702 va_list args;
14703 va_start(args, pcszMsg);
14704 HRESULT rc = setErrorInternal(aResultCode,
14705 getStaticClassIID(),
14706 getStaticComponentName(),
14707 Utf8Str(pcszMsg, args),
14708 false /* aWarning */,
14709 true /* aLogIt */);
14710 va_end(args);
14711 return rc;
14712}
14713
14714
14715HRESULT Machine::updateState(MachineState_T aState)
14716{
14717 NOREF(aState);
14718 ReturnComNotImplemented();
14719}
14720
14721HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14722{
14723 NOREF(aProgress);
14724 ReturnComNotImplemented();
14725}
14726
14727HRESULT Machine::endPowerUp(LONG aResult)
14728{
14729 NOREF(aResult);
14730 ReturnComNotImplemented();
14731}
14732
14733HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14734{
14735 NOREF(aProgress);
14736 ReturnComNotImplemented();
14737}
14738
14739HRESULT Machine::endPoweringDown(LONG aResult,
14740 const com::Utf8Str &aErrMsg)
14741{
14742 NOREF(aResult);
14743 NOREF(aErrMsg);
14744 ReturnComNotImplemented();
14745}
14746
14747HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14748 BOOL *aMatched,
14749 ULONG *aMaskedInterfaces)
14750{
14751 NOREF(aDevice);
14752 NOREF(aMatched);
14753 NOREF(aMaskedInterfaces);
14754 ReturnComNotImplemented();
14755
14756}
14757
14758HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14759{
14760 NOREF(aId); NOREF(aCaptureFilename);
14761 ReturnComNotImplemented();
14762}
14763
14764HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14765 BOOL aDone)
14766{
14767 NOREF(aId);
14768 NOREF(aDone);
14769 ReturnComNotImplemented();
14770}
14771
14772HRESULT Machine::autoCaptureUSBDevices()
14773{
14774 ReturnComNotImplemented();
14775}
14776
14777HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14778{
14779 NOREF(aDone);
14780 ReturnComNotImplemented();
14781}
14782
14783HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14784 ComPtr<IProgress> &aProgress)
14785{
14786 NOREF(aSession);
14787 NOREF(aProgress);
14788 ReturnComNotImplemented();
14789}
14790
14791HRESULT Machine::finishOnlineMergeMedium()
14792{
14793 ReturnComNotImplemented();
14794}
14795
14796HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14797 std::vector<com::Utf8Str> &aValues,
14798 std::vector<LONG64> &aTimestamps,
14799 std::vector<com::Utf8Str> &aFlags)
14800{
14801 NOREF(aNames);
14802 NOREF(aValues);
14803 NOREF(aTimestamps);
14804 NOREF(aFlags);
14805 ReturnComNotImplemented();
14806}
14807
14808HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14809 const com::Utf8Str &aValue,
14810 LONG64 aTimestamp,
14811 const com::Utf8Str &aFlags)
14812{
14813 NOREF(aName);
14814 NOREF(aValue);
14815 NOREF(aTimestamp);
14816 NOREF(aFlags);
14817 ReturnComNotImplemented();
14818}
14819
14820HRESULT Machine::lockMedia()
14821{
14822 ReturnComNotImplemented();
14823}
14824
14825HRESULT Machine::unlockMedia()
14826{
14827 ReturnComNotImplemented();
14828}
14829
14830HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14831 ComPtr<IMediumAttachment> &aNewAttachment)
14832{
14833 NOREF(aAttachment);
14834 NOREF(aNewAttachment);
14835 ReturnComNotImplemented();
14836}
14837
14838HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14839 ULONG aCpuUser,
14840 ULONG aCpuKernel,
14841 ULONG aCpuIdle,
14842 ULONG aMemTotal,
14843 ULONG aMemFree,
14844 ULONG aMemBalloon,
14845 ULONG aMemShared,
14846 ULONG aMemCache,
14847 ULONG aPagedTotal,
14848 ULONG aMemAllocTotal,
14849 ULONG aMemFreeTotal,
14850 ULONG aMemBalloonTotal,
14851 ULONG aMemSharedTotal,
14852 ULONG aVmNetRx,
14853 ULONG aVmNetTx)
14854{
14855 NOREF(aValidStats);
14856 NOREF(aCpuUser);
14857 NOREF(aCpuKernel);
14858 NOREF(aCpuIdle);
14859 NOREF(aMemTotal);
14860 NOREF(aMemFree);
14861 NOREF(aMemBalloon);
14862 NOREF(aMemShared);
14863 NOREF(aMemCache);
14864 NOREF(aPagedTotal);
14865 NOREF(aMemAllocTotal);
14866 NOREF(aMemFreeTotal);
14867 NOREF(aMemBalloonTotal);
14868 NOREF(aMemSharedTotal);
14869 NOREF(aVmNetRx);
14870 NOREF(aVmNetTx);
14871 ReturnComNotImplemented();
14872}
14873
14874HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14875 com::Utf8Str &aResult)
14876{
14877 NOREF(aAuthParams);
14878 NOREF(aResult);
14879 ReturnComNotImplemented();
14880}
14881
14882HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14883{
14884 NOREF(aFlags);
14885 ReturnComNotImplemented();
14886}
14887
14888/* This isn't handled entirely by the wrapper generator yet. */
14889#ifdef VBOX_WITH_XPCOM
14890NS_DECL_CLASSINFO(SessionMachine)
14891NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14892
14893NS_DECL_CLASSINFO(SnapshotMachine)
14894NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14895#endif
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