VirtualBox

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

Last change on this file since 47376 was 47376, checked in by vboxsync, 11 years ago

Main/USB: USB Controller implementation rework. Moved filter handling into a separate interface

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 495.6 KB
Line 
1/* $Id: MachineImpl.cpp 47376 2013-07-24 15:13:52Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "USBDeviceFiltersImpl.h"
44#include "HostImpl.h"
45#include "SharedFolderImpl.h"
46#include "GuestOSTypeImpl.h"
47#include "VirtualBoxErrorInfoImpl.h"
48#include "GuestImpl.h"
49#include "StorageControllerImpl.h"
50#include "DisplayImpl.h"
51#include "DisplayUtils.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76#include <iprt/base64.h>
77
78#include <VBox/com/array.h>
79#include <VBox/com/list.h>
80
81#include <VBox/err.h>
82#include <VBox/param.h>
83#include <VBox/settings.h>
84#include <VBox/vmm/ssm.h>
85
86#ifdef VBOX_WITH_GUEST_PROPS
87# include <VBox/HostServices/GuestPropertySvc.h>
88# include <VBox/com/array.h>
89#endif
90
91#include "VBox/com/MultiResult.h"
92
93#include <algorithm>
94
95#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
96# define HOSTSUFF_EXE ".exe"
97#else /* !RT_OS_WINDOWS */
98# define HOSTSUFF_EXE ""
99#endif /* !RT_OS_WINDOWS */
100
101// defines / prototypes
102/////////////////////////////////////////////////////////////////////////////
103
104/////////////////////////////////////////////////////////////////////////////
105// Machine::Data structure
106/////////////////////////////////////////////////////////////////////////////
107
108Machine::Data::Data()
109{
110 mRegistered = FALSE;
111 pMachineConfigFile = NULL;
112 /* Contains hints on what has changed when the user is using the VM (config
113 * changes, running the VM, ...). This is used to decide if a config needs
114 * to be written to disk. */
115 flModifications = 0;
116 /* VM modification usually also trigger setting the current state to
117 * "Modified". Although this is not always the case. An e.g. is the VM
118 * initialization phase or when snapshot related data is changed. The
119 * actually behavior is controlled by the following flag. */
120 m_fAllowStateModification = false;
121 mAccessible = FALSE;
122 /* mUuid is initialized in Machine::init() */
123
124 mMachineState = MachineState_PoweredOff;
125 RTTimeNow(&mLastStateChange);
126
127 mMachineStateDeps = 0;
128 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
129 mMachineStateChangePending = 0;
130
131 mCurrentStateModified = TRUE;
132 mGuestPropertiesModified = FALSE;
133
134 mSession.mPID = NIL_RTPROCESS;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
190 mHWVirtExExclusive = false;
191#else
192 mHWVirtExExclusive = true;
193#endif
194#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
195 mPAEEnabled = true;
196#else
197 mPAEEnabled = false;
198#endif
199 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
200 mSyntheticCpu = false;
201 mHPETEnabled = false;
202
203 /* default boot order: floppy - DVD - HDD */
204 mBootOrder[0] = DeviceType_Floppy;
205 mBootOrder[1] = DeviceType_DVD;
206 mBootOrder[2] = DeviceType_HardDisk;
207 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
208 mBootOrder[i] = DeviceType_Null;
209
210 mClipboardMode = ClipboardMode_Disabled;
211 mDragAndDropMode = DragAndDropMode_Disabled;
212 mGuestPropertyNotificationPatterns = "";
213
214 mFirmwareType = FirmwareType_BIOS;
215 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
216 mPointingHIDType = PointingHIDType_PS2Mouse;
217 mChipsetType = ChipsetType_PIIX3;
218 mEmulatedUSBWebcamEnabled = FALSE;
219 mEmulatedUSBCardReaderEnabled = FALSE;
220
221 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
222 mCPUAttached[i] = false;
223
224 mIOCacheEnabled = true;
225 mIOCacheSize = 5; /* 5MB */
226
227 /* Maximum CPU execution cap by default. */
228 mCpuExecutionCap = 100;
229}
230
231Machine::HWData::~HWData()
232{
233}
234
235/////////////////////////////////////////////////////////////////////////////
236// Machine::HDData structure
237/////////////////////////////////////////////////////////////////////////////
238
239Machine::MediaData::MediaData()
240{
241}
242
243Machine::MediaData::~MediaData()
244{
245}
246
247/////////////////////////////////////////////////////////////////////////////
248// Machine class
249/////////////////////////////////////////////////////////////////////////////
250
251// constructor / destructor
252/////////////////////////////////////////////////////////////////////////////
253
254Machine::Machine() :
255#ifdef VBOX_WITH_RESOURCE_USAGE_API
256 mCollectorGuest(NULL),
257#endif
258 mPeer(NULL),
259 mParent(NULL),
260 mSerialPorts(),
261 mParallelPorts(),
262 uRegistryNeedsSaving(0)
263{}
264
265Machine::~Machine()
266{}
267
268HRESULT Machine::FinalConstruct()
269{
270 LogFlowThisFunc(("\n"));
271 return BaseFinalConstruct();
272}
273
274void Machine::FinalRelease()
275{
276 LogFlowThisFunc(("\n"));
277 uninit();
278 BaseFinalRelease();
279}
280
281/**
282 * Initializes a new machine instance; this init() variant creates a new, empty machine.
283 * This gets called from VirtualBox::CreateMachine().
284 *
285 * @param aParent Associated parent object
286 * @param strConfigFile Local file system path to the VM settings file (can
287 * be relative to the VirtualBox config directory).
288 * @param strName name for the machine
289 * @param llGroups list of groups for the machine
290 * @param aOsType OS Type of this machine or NULL.
291 * @param aId UUID for the new machine.
292 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
293 *
294 * @return Success indicator. if not S_OK, the machine object is invalid
295 */
296HRESULT Machine::init(VirtualBox *aParent,
297 const Utf8Str &strConfigFile,
298 const Utf8Str &strName,
299 const StringsList &llGroups,
300 GuestOSType *aOsType,
301 const Guid &aId,
302 bool fForceOverwrite,
303 bool fDirectoryIncludesUUID)
304{
305 LogFlowThisFuncEnter();
306 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
307
308 /* Enclose the state transition NotReady->InInit->Ready */
309 AutoInitSpan autoInitSpan(this);
310 AssertReturn(autoInitSpan.isOk(), E_FAIL);
311
312 HRESULT rc = initImpl(aParent, strConfigFile);
313 if (FAILED(rc)) return rc;
314
315 rc = tryCreateMachineConfigFile(fForceOverwrite);
316 if (FAILED(rc)) return rc;
317
318 if (SUCCEEDED(rc))
319 {
320 // create an empty machine config
321 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
322
323 rc = initDataAndChildObjects();
324 }
325
326 if (SUCCEEDED(rc))
327 {
328 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
329 mData->mAccessible = TRUE;
330
331 unconst(mData->mUuid) = aId;
332
333 mUserData->s.strName = strName;
334
335 mUserData->s.llGroups = llGroups;
336
337 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
338 // the "name sync" flag determines whether the machine directory gets renamed along
339 // with the machine file; say so if the settings file name is the same as the
340 // settings file parent directory (machine directory)
341 mUserData->s.fNameSync = isInOwnDir();
342
343 // initialize the default snapshots folder
344 rc = COMSETTER(SnapshotFolder)(NULL);
345 AssertComRC(rc);
346
347 if (aOsType)
348 {
349 /* Store OS type */
350 mUserData->s.strOsType = aOsType->id();
351
352 /* Apply BIOS defaults */
353 mBIOSSettings->applyDefaults(aOsType);
354
355 /* Apply network adapters defaults */
356 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
357 mNetworkAdapters[slot]->applyDefaults(aOsType);
358
359 /* Apply serial port defaults */
360 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
361 mSerialPorts[slot]->applyDefaults(aOsType);
362
363 /* Let the OS type select 64-bit ness. */
364 mHWData->mLongMode = aOsType->is64Bit()
365 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
366 }
367
368 /* At this point the changing of the current state modification
369 * flag is allowed. */
370 allowStateModification();
371
372 /* commit all changes made during the initialization */
373 commit();
374 }
375
376 /* Confirm a successful initialization when it's the case */
377 if (SUCCEEDED(rc))
378 {
379 if (mData->mAccessible)
380 autoInitSpan.setSucceeded();
381 else
382 autoInitSpan.setLimited();
383 }
384
385 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
386 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
387 mData->mRegistered,
388 mData->mAccessible,
389 rc));
390
391 LogFlowThisFuncLeave();
392
393 return rc;
394}
395
396/**
397 * Initializes a new instance with data from machine XML (formerly Init_Registered).
398 * Gets called in two modes:
399 *
400 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
401 * UUID is specified and we mark the machine as "registered";
402 *
403 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
404 * and the machine remains unregistered until RegisterMachine() is called.
405 *
406 * @param aParent Associated parent object
407 * @param aConfigFile Local file system path to the VM settings file (can
408 * be relative to the VirtualBox config directory).
409 * @param aId UUID of the machine or NULL (see above).
410 *
411 * @return Success indicator. if not S_OK, the machine object is invalid
412 */
413HRESULT Machine::initFromSettings(VirtualBox *aParent,
414 const Utf8Str &strConfigFile,
415 const Guid *aId)
416{
417 LogFlowThisFuncEnter();
418 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
419
420 /* Enclose the state transition NotReady->InInit->Ready */
421 AutoInitSpan autoInitSpan(this);
422 AssertReturn(autoInitSpan.isOk(), E_FAIL);
423
424 HRESULT rc = initImpl(aParent, strConfigFile);
425 if (FAILED(rc)) return rc;
426
427 if (aId)
428 {
429 // loading a registered VM:
430 unconst(mData->mUuid) = *aId;
431 mData->mRegistered = TRUE;
432 // now load the settings from XML:
433 rc = registeredInit();
434 // this calls initDataAndChildObjects() and loadSettings()
435 }
436 else
437 {
438 // opening an unregistered VM (VirtualBox::OpenMachine()):
439 rc = initDataAndChildObjects();
440
441 if (SUCCEEDED(rc))
442 {
443 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
444 mData->mAccessible = TRUE;
445
446 try
447 {
448 // load and parse machine XML; this will throw on XML or logic errors
449 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
450
451 // reject VM UUID duplicates, they can happen if someone
452 // tries to register an already known VM config again
453 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
454 true /* fPermitInaccessible */,
455 false /* aDoSetError */,
456 NULL) != VBOX_E_OBJECT_NOT_FOUND)
457 {
458 throw setError(E_FAIL,
459 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
460 mData->m_strConfigFile.c_str());
461 }
462
463 // use UUID from machine config
464 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
465
466 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
467 NULL /* puuidRegistry */);
468 if (FAILED(rc)) throw rc;
469
470 /* At this point the changing of the current state modification
471 * flag is allowed. */
472 allowStateModification();
473
474 commit();
475 }
476 catch (HRESULT err)
477 {
478 /* we assume that error info is set by the thrower */
479 rc = err;
480 }
481 catch (...)
482 {
483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
484 }
485 }
486 }
487
488 /* Confirm a successful initialization when it's the case */
489 if (SUCCEEDED(rc))
490 {
491 if (mData->mAccessible)
492 autoInitSpan.setSucceeded();
493 else
494 {
495 autoInitSpan.setLimited();
496
497 // uninit media from this machine's media registry, or else
498 // reloading the settings will fail
499 mParent->unregisterMachineMedia(getId());
500 }
501 }
502
503 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
504 "rc=%08X\n",
505 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
506 mData->mRegistered, mData->mAccessible, rc));
507
508 LogFlowThisFuncLeave();
509
510 return rc;
511}
512
513/**
514 * Initializes a new instance from a machine config that is already in memory
515 * (import OVF case). Since we are importing, the UUID in the machine
516 * config is ignored and we always generate a fresh one.
517 *
518 * @param strName Name for the new machine; this overrides what is specified in config and is used
519 * for the settings file as well.
520 * @param config Machine configuration loaded and parsed from XML.
521 *
522 * @return Success indicator. if not S_OK, the machine object is invalid
523 */
524HRESULT Machine::init(VirtualBox *aParent,
525 const Utf8Str &strName,
526 const settings::MachineConfigFile &config)
527{
528 LogFlowThisFuncEnter();
529
530 /* Enclose the state transition NotReady->InInit->Ready */
531 AutoInitSpan autoInitSpan(this);
532 AssertReturn(autoInitSpan.isOk(), E_FAIL);
533
534 Utf8Str strConfigFile;
535 aParent->getDefaultMachineFolder(strConfigFile);
536 strConfigFile.append(RTPATH_DELIMITER);
537 strConfigFile.append(strName);
538 strConfigFile.append(RTPATH_DELIMITER);
539 strConfigFile.append(strName);
540 strConfigFile.append(".vbox");
541
542 HRESULT rc = initImpl(aParent, strConfigFile);
543 if (FAILED(rc)) return rc;
544
545 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
546 if (FAILED(rc)) return rc;
547
548 rc = initDataAndChildObjects();
549
550 if (SUCCEEDED(rc))
551 {
552 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
553 mData->mAccessible = TRUE;
554
555 // create empty machine config for instance data
556 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
557
558 // generate fresh UUID, ignore machine config
559 unconst(mData->mUuid).create();
560
561 rc = loadMachineDataFromSettings(config,
562 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
563
564 // override VM name as well, it may be different
565 mUserData->s.strName = strName;
566
567 if (SUCCEEDED(rc))
568 {
569 /* At this point the changing of the current state modification
570 * flag is allowed. */
571 allowStateModification();
572
573 /* commit all changes made during the initialization */
574 commit();
575 }
576 }
577
578 /* Confirm a successful initialization when it's the case */
579 if (SUCCEEDED(rc))
580 {
581 if (mData->mAccessible)
582 autoInitSpan.setSucceeded();
583 else
584 {
585 autoInitSpan.setLimited();
586
587 // uninit media from this machine's media registry, or else
588 // reloading the settings will fail
589 mParent->unregisterMachineMedia(getId());
590 }
591 }
592
593 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
594 "rc=%08X\n",
595 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
596 mData->mRegistered, mData->mAccessible, rc));
597
598 LogFlowThisFuncLeave();
599
600 return rc;
601}
602
603/**
604 * Shared code between the various init() implementations.
605 * @param aParent
606 * @return
607 */
608HRESULT Machine::initImpl(VirtualBox *aParent,
609 const Utf8Str &strConfigFile)
610{
611 LogFlowThisFuncEnter();
612
613 AssertReturn(aParent, E_INVALIDARG);
614 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
615
616 HRESULT rc = S_OK;
617
618 /* share the parent weakly */
619 unconst(mParent) = aParent;
620
621 /* allocate the essential machine data structure (the rest will be
622 * allocated later by initDataAndChildObjects() */
623 mData.allocate();
624
625 /* memorize the config file name (as provided) */
626 mData->m_strConfigFile = strConfigFile;
627
628 /* get the full file name */
629 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
630 if (RT_FAILURE(vrc1))
631 return setError(VBOX_E_FILE_ERROR,
632 tr("Invalid machine settings file name '%s' (%Rrc)"),
633 strConfigFile.c_str(),
634 vrc1);
635
636 LogFlowThisFuncLeave();
637
638 return rc;
639}
640
641/**
642 * Tries to create a machine settings file in the path stored in the machine
643 * instance data. Used when a new machine is created to fail gracefully if
644 * the settings file could not be written (e.g. because machine dir is read-only).
645 * @return
646 */
647HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
648{
649 HRESULT rc = S_OK;
650
651 // when we create a new machine, we must be able to create the settings file
652 RTFILE f = NIL_RTFILE;
653 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
654 if ( RT_SUCCESS(vrc)
655 || vrc == VERR_SHARING_VIOLATION
656 )
657 {
658 if (RT_SUCCESS(vrc))
659 RTFileClose(f);
660 if (!fForceOverwrite)
661 rc = setError(VBOX_E_FILE_ERROR,
662 tr("Machine settings file '%s' already exists"),
663 mData->m_strConfigFileFull.c_str());
664 else
665 {
666 /* try to delete the config file, as otherwise the creation
667 * of a new settings file will fail. */
668 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
669 if (RT_FAILURE(vrc2))
670 rc = setError(VBOX_E_FILE_ERROR,
671 tr("Could not delete the existing settings file '%s' (%Rrc)"),
672 mData->m_strConfigFileFull.c_str(), vrc2);
673 }
674 }
675 else if ( vrc != VERR_FILE_NOT_FOUND
676 && vrc != VERR_PATH_NOT_FOUND
677 )
678 rc = setError(VBOX_E_FILE_ERROR,
679 tr("Invalid machine settings file name '%s' (%Rrc)"),
680 mData->m_strConfigFileFull.c_str(),
681 vrc);
682 return rc;
683}
684
685/**
686 * Initializes the registered machine by loading the settings file.
687 * This method is separated from #init() in order to make it possible to
688 * retry the operation after VirtualBox startup instead of refusing to
689 * startup the whole VirtualBox server in case if the settings file of some
690 * registered VM is invalid or inaccessible.
691 *
692 * @note Must be always called from this object's write lock
693 * (unless called from #init() that doesn't need any locking).
694 * @note Locks the mUSBController method for writing.
695 * @note Subclasses must not call this method.
696 */
697HRESULT Machine::registeredInit()
698{
699 AssertReturn(!isSessionMachine(), E_FAIL);
700 AssertReturn(!isSnapshotMachine(), E_FAIL);
701 AssertReturn(mData->mUuid.isValid(), E_FAIL);
702 AssertReturn(!mData->mAccessible, E_FAIL);
703
704 HRESULT rc = initDataAndChildObjects();
705
706 if (SUCCEEDED(rc))
707 {
708 /* Temporarily reset the registered flag in order to let setters
709 * potentially called from loadSettings() succeed (isMutable() used in
710 * all setters will return FALSE for a Machine instance if mRegistered
711 * is TRUE). */
712 mData->mRegistered = FALSE;
713
714 try
715 {
716 // load and parse machine XML; this will throw on XML or logic errors
717 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
718
719 if (mData->mUuid != mData->pMachineConfigFile->uuid)
720 throw setError(E_FAIL,
721 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
722 mData->pMachineConfigFile->uuid.raw(),
723 mData->m_strConfigFileFull.c_str(),
724 mData->mUuid.toString().c_str(),
725 mParent->settingsFilePath().c_str());
726
727 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
728 NULL /* const Guid *puuidRegistry */);
729 if (FAILED(rc)) throw rc;
730 }
731 catch (HRESULT err)
732 {
733 /* we assume that error info is set by the thrower */
734 rc = err;
735 }
736 catch (...)
737 {
738 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
739 }
740
741 /* Restore the registered flag (even on failure) */
742 mData->mRegistered = TRUE;
743 }
744
745 if (SUCCEEDED(rc))
746 {
747 /* Set mAccessible to TRUE only if we successfully locked and loaded
748 * the settings file */
749 mData->mAccessible = TRUE;
750
751 /* commit all changes made during loading the settings file */
752 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
753 /// @todo r=klaus for some reason the settings loading logic backs up
754 // the settings, and therefore a commit is needed. Should probably be changed.
755 }
756 else
757 {
758 /* If the machine is registered, then, instead of returning a
759 * failure, we mark it as inaccessible and set the result to
760 * success to give it a try later */
761
762 /* fetch the current error info */
763 mData->mAccessError = com::ErrorInfo();
764 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
765 mData->mUuid.raw(),
766 mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->unregisterMachineMedia(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 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(!isSnapshotMachine());
819 Assert(!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 LogWarningThisFunc(("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 LogWarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->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(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->unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
878 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// IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892
893STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
894{
895 CheckComArgOutPointerValid(aParent);
896
897 AutoLimitedCaller autoCaller(this);
898 if (FAILED(autoCaller.rc())) return autoCaller.rc();
899
900 /* mParent is constant during life time, no need to lock */
901 ComObjPtr<VirtualBox> pVirtualBox(mParent);
902 pVirtualBox.queryInterfaceTo(aParent);
903
904 return S_OK;
905}
906
907STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
908{
909 CheckComArgOutPointerValid(aAccessible);
910
911 AutoLimitedCaller autoCaller(this);
912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
913
914 LogFlowThisFunc(("ENTER\n"));
915
916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
917
918 HRESULT rc = S_OK;
919
920 if (!mData->mAccessible)
921 {
922 /* try to initialize the VM once more if not accessible */
923
924 AutoReinitSpan autoReinitSpan(this);
925 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
926
927#ifdef DEBUG
928 LogFlowThisFunc(("Dumping media backreferences\n"));
929 mParent->dumpAllBackRefs();
930#endif
931
932 if (mData->pMachineConfigFile)
933 {
934 // reset the XML file to force loadSettings() (called from registeredInit())
935 // to parse it again; the file might have changed
936 delete mData->pMachineConfigFile;
937 mData->pMachineConfigFile = NULL;
938 }
939
940 rc = registeredInit();
941
942 if (SUCCEEDED(rc) && mData->mAccessible)
943 {
944 autoReinitSpan.setSucceeded();
945
946 /* make sure interesting parties will notice the accessibility
947 * state change */
948 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
949 mParent->onMachineDataChange(mData->mUuid);
950 }
951 }
952
953 if (SUCCEEDED(rc))
954 *aAccessible = mData->mAccessible;
955
956 LogFlowThisFuncLeave();
957
958 return rc;
959}
960
961STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
962{
963 CheckComArgOutPointerValid(aAccessError);
964
965 AutoLimitedCaller autoCaller(this);
966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
967
968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
969
970 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
971 {
972 /* return shortly */
973 aAccessError = NULL;
974 return S_OK;
975 }
976
977 HRESULT rc = S_OK;
978
979 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
980 rc = errorInfo.createObject();
981 if (SUCCEEDED(rc))
982 {
983 errorInfo->init(mData->mAccessError.getResultCode(),
984 mData->mAccessError.getInterfaceID().ref(),
985 Utf8Str(mData->mAccessError.getComponent()).c_str(),
986 Utf8Str(mData->mAccessError.getText()));
987 rc = errorInfo.queryInterfaceTo(aAccessError);
988 }
989
990 return rc;
991}
992
993STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
994{
995 CheckComArgOutPointerValid(aName);
996
997 AutoCaller autoCaller(this);
998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
999
1000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1001
1002 mUserData->s.strName.cloneTo(aName);
1003
1004 return S_OK;
1005}
1006
1007STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1008{
1009 CheckComArgStrNotEmptyOrNull(aName);
1010
1011 AutoCaller autoCaller(this);
1012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1013
1014 // prohibit setting a UUID only as the machine name, or else it can
1015 // never be found by findMachine()
1016 Guid test(aName);
1017
1018 if (test.isValid())
1019 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1020
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 HRESULT rc = checkStateDependency(MutableStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strName = aName;
1029
1030 return S_OK;
1031}
1032
1033STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1034{
1035 CheckComArgOutPointerValid(aDescription);
1036
1037 AutoCaller autoCaller(this);
1038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1039
1040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1041
1042 mUserData->s.strDescription.cloneTo(aDescription);
1043
1044 return S_OK;
1045}
1046
1047STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1048{
1049 AutoCaller autoCaller(this);
1050 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1051
1052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1053
1054 // this can be done in principle in any state as it doesn't affect the VM
1055 // significantly, but play safe by not messing around while complex
1056 // activities are going on
1057 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1058 if (FAILED(rc)) return rc;
1059
1060 setModified(IsModified_MachineData);
1061 mUserData.backup();
1062 mUserData->s.strDescription = aDescription;
1063
1064 return S_OK;
1065}
1066
1067STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1068{
1069 CheckComArgOutPointerValid(aId);
1070
1071 AutoLimitedCaller autoCaller(this);
1072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1073
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 mData->mUuid.toUtf16().cloneTo(aId);
1077
1078 return S_OK;
1079}
1080
1081STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1082{
1083 CheckComArgOutSafeArrayPointerValid(aGroups);
1084
1085 AutoCaller autoCaller(this);
1086 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1087
1088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1089 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1090 size_t i = 0;
1091 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1092 it != mUserData->s.llGroups.end();
1093 ++it, i++)
1094 {
1095 Bstr tmp = *it;
1096 tmp.cloneTo(&groups[i]);
1097 }
1098 groups.detachTo(ComSafeArrayOutArg(aGroups));
1099
1100 return S_OK;
1101}
1102
1103STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1104{
1105 AutoCaller autoCaller(this);
1106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1107
1108 StringsList llGroups;
1109 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1110 if (FAILED(rc))
1111 return rc;
1112
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 // changing machine groups is possible while the VM is offline
1116 rc = checkStateDependency(OfflineStateDep);
1117 if (FAILED(rc)) return rc;
1118
1119 setModified(IsModified_MachineData);
1120 mUserData.backup();
1121 mUserData->s.llGroups = llGroups;
1122
1123 return S_OK;
1124}
1125
1126STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1127{
1128 CheckComArgOutPointerValid(aOSTypeId);
1129
1130 AutoCaller autoCaller(this);
1131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1132
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 mUserData->s.strOsType.cloneTo(aOSTypeId);
1136
1137 return S_OK;
1138}
1139
1140STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1141{
1142 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1143
1144 AutoCaller autoCaller(this);
1145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1146
1147 /* look up the object by Id to check it is valid */
1148 ComPtr<IGuestOSType> guestOSType;
1149 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1150 if (FAILED(rc)) return rc;
1151
1152 /* when setting, always use the "etalon" value for consistency -- lookup
1153 * by ID is case-insensitive and the input value may have different case */
1154 Bstr osTypeId;
1155 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1156 if (FAILED(rc)) return rc;
1157
1158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1159
1160 rc = checkStateDependency(MutableStateDep);
1161 if (FAILED(rc)) return rc;
1162
1163 setModified(IsModified_MachineData);
1164 mUserData.backup();
1165 mUserData->s.strOsType = osTypeId;
1166
1167 return S_OK;
1168}
1169
1170
1171STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1172{
1173 CheckComArgOutPointerValid(aFirmwareType);
1174
1175 AutoCaller autoCaller(this);
1176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1177
1178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1179
1180 *aFirmwareType = mHWData->mFirmwareType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1186{
1187 AutoCaller autoCaller(this);
1188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1190
1191 HRESULT rc = checkStateDependency(MutableStateDep);
1192 if (FAILED(rc)) return rc;
1193
1194 setModified(IsModified_MachineData);
1195 mHWData.backup();
1196 mHWData->mFirmwareType = aFirmwareType;
1197
1198 return S_OK;
1199}
1200
1201STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1202{
1203 CheckComArgOutPointerValid(aKeyboardHIDType);
1204
1205 AutoCaller autoCaller(this);
1206 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1207
1208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1209
1210 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1211
1212 return S_OK;
1213}
1214
1215STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1216{
1217 AutoCaller autoCaller(this);
1218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1220
1221 HRESULT rc = checkStateDependency(MutableStateDep);
1222 if (FAILED(rc)) return rc;
1223
1224 setModified(IsModified_MachineData);
1225 mHWData.backup();
1226 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1227
1228 return S_OK;
1229}
1230
1231STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1232{
1233 CheckComArgOutPointerValid(aPointingHIDType);
1234
1235 AutoCaller autoCaller(this);
1236 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1237
1238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1239
1240 *aPointingHIDType = mHWData->mPointingHIDType;
1241
1242 return S_OK;
1243}
1244
1245STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1246{
1247 AutoCaller autoCaller(this);
1248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 HRESULT rc = checkStateDependency(MutableStateDep);
1252 if (FAILED(rc)) return rc;
1253
1254 setModified(IsModified_MachineData);
1255 mHWData.backup();
1256 mHWData->mPointingHIDType = aPointingHIDType;
1257
1258 return S_OK;
1259}
1260
1261STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1262{
1263 CheckComArgOutPointerValid(aChipsetType);
1264
1265 AutoCaller autoCaller(this);
1266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1267
1268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1269
1270 *aChipsetType = mHWData->mChipsetType;
1271
1272 return S_OK;
1273}
1274
1275STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1276{
1277 AutoCaller autoCaller(this);
1278 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1279 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1280
1281 HRESULT rc = checkStateDependency(MutableStateDep);
1282 if (FAILED(rc)) return rc;
1283
1284 if (aChipsetType != mHWData->mChipsetType)
1285 {
1286 setModified(IsModified_MachineData);
1287 mHWData.backup();
1288 mHWData->mChipsetType = aChipsetType;
1289
1290 // Resize network adapter array, to be finalized on commit/rollback.
1291 // We must not throw away entries yet, otherwise settings are lost
1292 // without a way to roll back.
1293 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1294 size_t oldCount = mNetworkAdapters.size();
1295 if (newCount > oldCount)
1296 {
1297 mNetworkAdapters.resize(newCount);
1298 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1299 {
1300 unconst(mNetworkAdapters[slot]).createObject();
1301 mNetworkAdapters[slot]->init(this, slot);
1302 }
1303 }
1304 }
1305
1306 return S_OK;
1307}
1308
1309STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1310{
1311 CheckComArgOutPointerValid(aHWVersion);
1312
1313 AutoCaller autoCaller(this);
1314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1315
1316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1317
1318 mHWData->mHWVersion.cloneTo(aHWVersion);
1319
1320 return S_OK;
1321}
1322
1323STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1324{
1325 /* check known version */
1326 Utf8Str hwVersion = aHWVersion;
1327 if ( hwVersion.compare("1") != 0
1328 && hwVersion.compare("2") != 0)
1329 return setError(E_INVALIDARG,
1330 tr("Invalid hardware version: %ls\n"), aHWVersion);
1331
1332 AutoCaller autoCaller(this);
1333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1334
1335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1336
1337 HRESULT rc = checkStateDependency(MutableStateDep);
1338 if (FAILED(rc)) return rc;
1339
1340 setModified(IsModified_MachineData);
1341 mHWData.backup();
1342 mHWData->mHWVersion = hwVersion;
1343
1344 return S_OK;
1345}
1346
1347STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1348{
1349 CheckComArgOutPointerValid(aUUID);
1350
1351 AutoCaller autoCaller(this);
1352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1353
1354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1355
1356 if (mHWData->mHardwareUUID.isValid())
1357 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1358 else
1359 mData->mUuid.toUtf16().cloneTo(aUUID);
1360
1361 return S_OK;
1362}
1363
1364STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1365{
1366 Guid hardwareUUID(aUUID);
1367 if (!hardwareUUID.isValid())
1368 return E_INVALIDARG;
1369
1370 AutoCaller autoCaller(this);
1371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1372
1373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1374
1375 HRESULT rc = checkStateDependency(MutableStateDep);
1376 if (FAILED(rc)) return rc;
1377
1378 setModified(IsModified_MachineData);
1379 mHWData.backup();
1380 if (hardwareUUID == mData->mUuid)
1381 mHWData->mHardwareUUID.clear();
1382 else
1383 mHWData->mHardwareUUID = hardwareUUID;
1384
1385 return S_OK;
1386}
1387
1388STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1389{
1390 CheckComArgOutPointerValid(memorySize);
1391
1392 AutoCaller autoCaller(this);
1393 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1394
1395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1396
1397 *memorySize = mHWData->mMemorySize;
1398
1399 return S_OK;
1400}
1401
1402STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1403{
1404 /* check RAM limits */
1405 if ( memorySize < MM_RAM_MIN_IN_MB
1406 || memorySize > MM_RAM_MAX_IN_MB
1407 )
1408 return setError(E_INVALIDARG,
1409 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1410 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1411
1412 AutoCaller autoCaller(this);
1413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1414
1415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1416
1417 HRESULT rc = checkStateDependency(MutableStateDep);
1418 if (FAILED(rc)) return rc;
1419
1420 setModified(IsModified_MachineData);
1421 mHWData.backup();
1422 mHWData->mMemorySize = memorySize;
1423
1424 return S_OK;
1425}
1426
1427STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1428{
1429 CheckComArgOutPointerValid(CPUCount);
1430
1431 AutoCaller autoCaller(this);
1432 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1433
1434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1435
1436 *CPUCount = mHWData->mCPUCount;
1437
1438 return S_OK;
1439}
1440
1441STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1442{
1443 /* check CPU limits */
1444 if ( CPUCount < SchemaDefs::MinCPUCount
1445 || CPUCount > SchemaDefs::MaxCPUCount
1446 )
1447 return setError(E_INVALIDARG,
1448 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1449 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1450
1451 AutoCaller autoCaller(this);
1452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1457 if (mHWData->mCPUHotPlugEnabled)
1458 {
1459 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1460 {
1461 if (mHWData->mCPUAttached[idx])
1462 return setError(E_INVALIDARG,
1463 tr("There is still a CPU attached to socket %lu."
1464 "Detach the CPU before removing the socket"),
1465 CPUCount, idx+1);
1466 }
1467 }
1468
1469 HRESULT rc = checkStateDependency(MutableStateDep);
1470 if (FAILED(rc)) return rc;
1471
1472 setModified(IsModified_MachineData);
1473 mHWData.backup();
1474 mHWData->mCPUCount = CPUCount;
1475
1476 return S_OK;
1477}
1478
1479STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1480{
1481 CheckComArgOutPointerValid(aExecutionCap);
1482
1483 AutoCaller autoCaller(this);
1484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1485
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aExecutionCap = mHWData->mCpuExecutionCap;
1489
1490 return S_OK;
1491}
1492
1493STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1494{
1495 HRESULT rc = S_OK;
1496
1497 /* check throttle limits */
1498 if ( aExecutionCap < 1
1499 || aExecutionCap > 100
1500 )
1501 return setError(E_INVALIDARG,
1502 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1503 aExecutionCap, 1, 100);
1504
1505 AutoCaller autoCaller(this);
1506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 alock.release();
1511 rc = onCPUExecutionCapChange(aExecutionCap);
1512 alock.acquire();
1513 if (FAILED(rc)) return rc;
1514
1515 setModified(IsModified_MachineData);
1516 mHWData.backup();
1517 mHWData->mCpuExecutionCap = aExecutionCap;
1518
1519 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1520 if (Global::IsOnline(mData->mMachineState))
1521 saveSettings(NULL);
1522
1523 return S_OK;
1524}
1525
1526
1527STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled)
1528{
1529 CheckComArgOutPointerValid(aEnabled);
1530
1531 AutoCaller autoCaller(this);
1532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1533
1534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 *aEnabled = mHWData->mCPUHotPlugEnabled;
1537
1538 return S_OK;
1539}
1540
1541STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled)
1542{
1543 HRESULT rc = S_OK;
1544
1545 AutoCaller autoCaller(this);
1546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1547
1548 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1549
1550 rc = checkStateDependency(MutableStateDep);
1551 if (FAILED(rc)) return rc;
1552
1553 if (mHWData->mCPUHotPlugEnabled != aEnabled)
1554 {
1555 if (aEnabled)
1556 {
1557 setModified(IsModified_MachineData);
1558 mHWData.backup();
1559
1560 /* Add the amount of CPUs currently attached */
1561 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1562 {
1563 mHWData->mCPUAttached[i] = true;
1564 }
1565 }
1566 else
1567 {
1568 /*
1569 * We can disable hotplug only if the amount of maximum CPUs is equal
1570 * to the amount of attached CPUs
1571 */
1572 unsigned cCpusAttached = 0;
1573 unsigned iHighestId = 0;
1574
1575 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1576 {
1577 if (mHWData->mCPUAttached[i])
1578 {
1579 cCpusAttached++;
1580 iHighestId = i;
1581 }
1582 }
1583
1584 if ( (cCpusAttached != mHWData->mCPUCount)
1585 || (iHighestId >= mHWData->mCPUCount))
1586 return setError(E_INVALIDARG,
1587 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1588
1589 setModified(IsModified_MachineData);
1590 mHWData.backup();
1591 }
1592 }
1593
1594 mHWData->mCPUHotPlugEnabled = aEnabled;
1595
1596 return rc;
1597}
1598
1599STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled)
1600{
1601#ifdef VBOX_WITH_USB_CARDREADER
1602 CheckComArgOutPointerValid(aEnabled);
1603
1604 AutoCaller autoCaller(this);
1605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1606
1607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1608
1609 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1610
1611 return S_OK;
1612#else
1613 NOREF(aEnabled);
1614 return E_NOTIMPL;
1615#endif
1616}
1617
1618STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled)
1619{
1620#ifdef VBOX_WITH_USB_CARDREADER
1621 AutoCaller autoCaller(this);
1622 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1624
1625 HRESULT rc = checkStateDependency(MutableStateDep);
1626 if (FAILED(rc)) return rc;
1627
1628 setModified(IsModified_MachineData);
1629 mHWData.backup();
1630 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled;
1631
1632 return S_OK;
1633#else
1634 NOREF(aEnabled);
1635 return E_NOTIMPL;
1636#endif
1637}
1638
1639STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled)
1640{
1641#ifdef VBOX_WITH_USB_VIDEO
1642 CheckComArgOutPointerValid(aEnabled);
1643
1644 AutoCaller autoCaller(this);
1645 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1646
1647 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1648
1649 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled;
1650
1651 return S_OK;
1652#else
1653 NOREF(aEnabled);
1654 return E_NOTIMPL;
1655#endif
1656}
1657
1658STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled)
1659{
1660#ifdef VBOX_WITH_USB_VIDEO
1661 AutoCaller autoCaller(this);
1662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1664
1665 HRESULT rc = checkStateDependency(MutableStateDep);
1666 if (FAILED(rc)) return rc;
1667
1668 setModified(IsModified_MachineData);
1669 mHWData.backup();
1670 mHWData->mEmulatedUSBWebcamEnabled = aEnabled;
1671
1672 return S_OK;
1673#else
1674 NOREF(aEnabled);
1675 return E_NOTIMPL;
1676#endif
1677}
1678
1679STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled)
1680{
1681 CheckComArgOutPointerValid(aEnabled);
1682
1683 AutoCaller autoCaller(this);
1684 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1686
1687 *aEnabled = mHWData->mHPETEnabled;
1688
1689 return S_OK;
1690}
1691
1692STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled)
1693{
1694 HRESULT rc = S_OK;
1695
1696 AutoCaller autoCaller(this);
1697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 rc = checkStateDependency(MutableStateDep);
1701 if (FAILED(rc)) return rc;
1702
1703 setModified(IsModified_MachineData);
1704 mHWData.backup();
1705
1706 mHWData->mHPETEnabled = aEnabled;
1707
1708 return rc;
1709}
1710
1711STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1712{
1713 AutoCaller autoCaller(this);
1714 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1715
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 *fEnabled = mHWData->mVideoCaptureEnabled;
1719 return S_OK;
1720}
1721
1722STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1723{
1724 HRESULT rc = S_OK;
1725
1726 AutoCaller autoCaller(this);
1727 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1728 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1729
1730 setModified(IsModified_MachineData);
1731 mHWData.backup();
1732 mHWData->mVideoCaptureEnabled = fEnabled;
1733
1734 alock.release();
1735 rc = onVideoCaptureChange();
1736 alock.acquire();
1737 if (FAILED(rc))
1738 {
1739 /*
1740 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded.
1741 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1742 * determine if it should start or stop capturing. Therefore we need to manually
1743 * undo change.
1744 */
1745 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1746 return rc;
1747 }
1748
1749 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1750 if (Global::IsOnline(mData->mMachineState))
1751 saveSettings(NULL);
1752
1753 return rc;
1754}
1755
1756STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens))
1757{
1758 CheckComArgOutSafeArrayPointerValid(aScreens);
1759
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 SafeArray<BOOL> screens(mHWData->mMonitorCount);
1766 for (unsigned i = 0; i < screens.size(); i++)
1767 screens[i] = mHWData->maVideoCaptureScreens[i];
1768 screens.detachTo(ComSafeArrayOutArg(aScreens));
1769 return S_OK;
1770}
1771
1772STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens))
1773{
1774 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens));
1775 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1776 bool fChanged = false;
1777
1778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1779
1780 for (unsigned i = 0; i < screens.size(); i++)
1781 {
1782 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i]))
1783 {
1784 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]);
1785 fChanged = true;
1786 }
1787 }
1788 if (fChanged)
1789 {
1790 alock.release();
1791 HRESULT rc = onVideoCaptureChange();
1792 alock.acquire();
1793 if (FAILED(rc)) return rc;
1794 setModified(IsModified_MachineData);
1795
1796 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1797 if (Global::IsOnline(mData->mMachineState))
1798 saveSettings(NULL);
1799 }
1800
1801 return S_OK;
1802}
1803
1804STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile)
1805{
1806 AutoCaller autoCaller(this);
1807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1808
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810 if (mHWData->mVideoCaptureFile.isEmpty())
1811 {
1812 Utf8Str defaultFile;
1813 getDefaultVideoCaptureFile(defaultFile);
1814 defaultFile.cloneTo(apFile);
1815 }
1816 else
1817 mHWData->mVideoCaptureFile.cloneTo(apFile);
1818 return S_OK;
1819}
1820
1821STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1822{
1823 Utf8Str strFile(aFile);
1824 AutoCaller autoCaller(this);
1825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1826
1827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1828
1829 if ( Global::IsOnline(mData->mMachineState)
1830 && mHWData->mVideoCaptureEnabled)
1831 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1832
1833 if (!RTPathStartsWithRoot(strFile.c_str()))
1834 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1835
1836 if (!strFile.isEmpty())
1837 {
1838 Utf8Str defaultFile;
1839 getDefaultVideoCaptureFile(defaultFile);
1840 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1841 strFile.setNull();
1842 }
1843
1844 setModified(IsModified_MachineData);
1845 mHWData.backup();
1846 mHWData->mVideoCaptureFile = strFile;
1847
1848 return S_OK;
1849}
1850
1851STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes)
1852{
1853 AutoCaller autoCaller(this);
1854 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1855
1856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1857 *aHorzRes = mHWData->mVideoCaptureWidth;
1858 return S_OK;
1859}
1860
1861STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes)
1862{
1863 AutoCaller autoCaller(this);
1864 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1865
1866 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1867
1868 if ( Global::IsOnline(mData->mMachineState)
1869 && mHWData->mVideoCaptureEnabled)
1870 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1871
1872 setModified(IsModified_MachineData);
1873 mHWData.backup();
1874 mHWData->mVideoCaptureWidth = aHorzRes;
1875
1876 return S_OK;
1877}
1878
1879STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes)
1880{
1881 AutoCaller autoCaller(this);
1882 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1883
1884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1885 *aVertRes = mHWData->mVideoCaptureHeight;
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes)
1890{
1891 AutoCaller autoCaller(this);
1892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1893
1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1895
1896 if ( Global::IsOnline(mData->mMachineState)
1897 && mHWData->mVideoCaptureEnabled)
1898 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1899
1900 setModified(IsModified_MachineData);
1901 mHWData.backup();
1902 mHWData->mVideoCaptureHeight = aVertRes;
1903
1904 return S_OK;
1905}
1906
1907STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate)
1908{
1909 AutoCaller autoCaller(this);
1910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1911
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913 *aRate = mHWData->mVideoCaptureRate;
1914 return S_OK;
1915}
1916
1917STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate)
1918{
1919 AutoCaller autoCaller(this);
1920 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1921
1922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1923
1924 if ( Global::IsOnline(mData->mMachineState)
1925 && mHWData->mVideoCaptureEnabled)
1926 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1927
1928 setModified(IsModified_MachineData);
1929 mHWData.backup();
1930 mHWData->mVideoCaptureRate = aRate;
1931
1932 return S_OK;
1933}
1934
1935STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS)
1936{
1937 AutoCaller autoCaller(this);
1938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1939
1940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1941 *aFPS = mHWData->mVideoCaptureFPS;
1942 return S_OK;
1943}
1944
1945STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS)
1946{
1947 AutoCaller autoCaller(this);
1948 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1949
1950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1951
1952 if ( Global::IsOnline(mData->mMachineState)
1953 && mHWData->mVideoCaptureEnabled)
1954 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1955
1956 setModified(IsModified_MachineData);
1957 mHWData.backup();
1958 mHWData->mVideoCaptureFPS = aFPS;
1959
1960 return S_OK;
1961}
1962
1963STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1964{
1965 CheckComArgOutPointerValid(aGraphicsControllerType);
1966
1967 AutoCaller autoCaller(this);
1968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1969
1970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1971
1972 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1973
1974 return S_OK;
1975}
1976
1977STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1978{
1979 switch (aGraphicsControllerType)
1980 {
1981 case GraphicsControllerType_Null:
1982 case GraphicsControllerType_VBoxVGA:
1983 break;
1984 default:
1985 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1986 }
1987
1988 AutoCaller autoCaller(this);
1989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1990
1991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 HRESULT rc = checkStateDependency(MutableStateDep);
1994 if (FAILED(rc)) return rc;
1995
1996 setModified(IsModified_MachineData);
1997 mHWData.backup();
1998 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1999
2000 return S_OK;
2001}
2002
2003STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
2004{
2005 CheckComArgOutPointerValid(memorySize);
2006
2007 AutoCaller autoCaller(this);
2008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2009
2010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2011
2012 *memorySize = mHWData->mVRAMSize;
2013
2014 return S_OK;
2015}
2016
2017STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
2018{
2019 /* check VRAM limits */
2020 if (memorySize < SchemaDefs::MinGuestVRAM ||
2021 memorySize > SchemaDefs::MaxGuestVRAM)
2022 return setError(E_INVALIDARG,
2023 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2024 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2025
2026 AutoCaller autoCaller(this);
2027 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2028
2029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 HRESULT rc = checkStateDependency(MutableStateDep);
2032 if (FAILED(rc)) return rc;
2033
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mVRAMSize = memorySize;
2037
2038 return S_OK;
2039}
2040
2041/** @todo this method should not be public */
2042STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
2043{
2044 CheckComArgOutPointerValid(memoryBalloonSize);
2045
2046 AutoCaller autoCaller(this);
2047 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2048
2049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2050
2051 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
2052
2053 return S_OK;
2054}
2055
2056/**
2057 * Set the memory balloon size.
2058 *
2059 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2060 * we have to make sure that we never call IGuest from here.
2061 */
2062STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
2063{
2064 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2065#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2066 /* check limits */
2067 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2068 return setError(E_INVALIDARG,
2069 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2070 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2071
2072 AutoCaller autoCaller(this);
2073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2074
2075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 setModified(IsModified_MachineData);
2078 mHWData.backup();
2079 mHWData->mMemoryBalloonSize = memoryBalloonSize;
2080
2081 return S_OK;
2082#else
2083 NOREF(memoryBalloonSize);
2084 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2085#endif
2086}
2087
2088STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled)
2089{
2090 CheckComArgOutPointerValid(aEnabled);
2091
2092 AutoCaller autoCaller(this);
2093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2094
2095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2096
2097 *aEnabled = mHWData->mPageFusionEnabled;
2098 return S_OK;
2099}
2100
2101STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled)
2102{
2103#ifdef VBOX_WITH_PAGE_SHARING
2104 AutoCaller autoCaller(this);
2105 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2106
2107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2108
2109 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2110 setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mPageFusionEnabled = aEnabled;
2113 return S_OK;
2114#else
2115 NOREF(aEnabled);
2116 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2117#endif
2118}
2119
2120STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled)
2121{
2122 CheckComArgOutPointerValid(aEnabled);
2123
2124 AutoCaller autoCaller(this);
2125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2126
2127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 *aEnabled = mHWData->mAccelerate3DEnabled;
2130
2131 return S_OK;
2132}
2133
2134STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
2135{
2136 AutoCaller autoCaller(this);
2137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2138
2139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2140
2141 HRESULT rc = checkStateDependency(MutableStateDep);
2142 if (FAILED(rc)) return rc;
2143
2144 /** @todo check validity! */
2145
2146 setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mAccelerate3DEnabled = enable;
2149
2150 return S_OK;
2151}
2152
2153
2154STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled)
2155{
2156 CheckComArgOutPointerValid(aEnabled);
2157
2158 AutoCaller autoCaller(this);
2159 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2160
2161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 *aEnabled = mHWData->mAccelerate2DVideoEnabled;
2164
2165 return S_OK;
2166}
2167
2168STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
2169{
2170 AutoCaller autoCaller(this);
2171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2172
2173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2174
2175 HRESULT rc = checkStateDependency(MutableStateDep);
2176 if (FAILED(rc)) return rc;
2177
2178 /** @todo check validity! */
2179
2180 setModified(IsModified_MachineData);
2181 mHWData.backup();
2182 mHWData->mAccelerate2DVideoEnabled = enable;
2183
2184 return S_OK;
2185}
2186
2187STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2188{
2189 CheckComArgOutPointerValid(monitorCount);
2190
2191 AutoCaller autoCaller(this);
2192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2193
2194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2195
2196 *monitorCount = mHWData->mMonitorCount;
2197
2198 return S_OK;
2199}
2200
2201STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2202{
2203 /* make sure monitor count is a sensible number */
2204 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2205 return setError(E_INVALIDARG,
2206 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2207 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2208
2209 AutoCaller autoCaller(this);
2210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2211
2212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2213
2214 HRESULT rc = checkStateDependency(MutableStateDep);
2215 if (FAILED(rc)) return rc;
2216
2217 setModified(IsModified_MachineData);
2218 mHWData.backup();
2219 mHWData->mMonitorCount = monitorCount;
2220
2221 return S_OK;
2222}
2223
2224STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2225{
2226 CheckComArgOutPointerValid(biosSettings);
2227
2228 AutoCaller autoCaller(this);
2229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2230
2231 /* mBIOSSettings is constant during life time, no need to lock */
2232 mBIOSSettings.queryInterfaceTo(biosSettings);
2233
2234 return S_OK;
2235}
2236
2237STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2238{
2239 CheckComArgOutPointerValid(aVal);
2240
2241 AutoCaller autoCaller(this);
2242 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2243
2244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2245
2246 switch (property)
2247 {
2248 case CPUPropertyType_PAE:
2249 *aVal = mHWData->mPAEEnabled;
2250 break;
2251
2252 case CPUPropertyType_Synthetic:
2253 *aVal = mHWData->mSyntheticCpu;
2254 break;
2255
2256 case CPUPropertyType_LongMode:
2257 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2258 *aVal = TRUE;
2259 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2260 *aVal = FALSE;
2261#if HC_ARCH_BITS == 64
2262 else
2263 *aVal = TRUE;
2264#else
2265 else
2266 {
2267 *aVal = FALSE;
2268
2269 ComPtr<IGuestOSType> ptrGuestOSType;
2270 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2271 if (SUCCEEDED(hrc2))
2272 {
2273 BOOL fIs64Bit = FALSE;
2274 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2275 if (SUCCEEDED(hrc2) && fIs64Bit)
2276 {
2277 ComObjPtr<Host> ptrHost = mParent->host();
2278 alock.release();
2279
2280 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2281 if (FAILED(hrc2))
2282 *aVal = FALSE;
2283 }
2284 }
2285 }
2286#endif
2287 break;
2288
2289 default:
2290 return E_INVALIDARG;
2291 }
2292 return S_OK;
2293}
2294
2295STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2296{
2297 AutoCaller autoCaller(this);
2298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2299
2300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2301
2302 HRESULT rc = checkStateDependency(MutableStateDep);
2303 if (FAILED(rc)) return rc;
2304
2305 switch (property)
2306 {
2307 case CPUPropertyType_PAE:
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mPAEEnabled = !!aVal;
2311 break;
2312
2313 case CPUPropertyType_Synthetic:
2314 setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mSyntheticCpu = !!aVal;
2317 break;
2318
2319 case CPUPropertyType_LongMode:
2320 setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2323 break;
2324
2325 default:
2326 return E_INVALIDARG;
2327 }
2328 return S_OK;
2329}
2330
2331STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2332{
2333 CheckComArgOutPointerValid(aValEax);
2334 CheckComArgOutPointerValid(aValEbx);
2335 CheckComArgOutPointerValid(aValEcx);
2336 CheckComArgOutPointerValid(aValEdx);
2337
2338 AutoCaller autoCaller(this);
2339 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2340
2341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 switch(aId)
2344 {
2345 case 0x0:
2346 case 0x1:
2347 case 0x2:
2348 case 0x3:
2349 case 0x4:
2350 case 0x5:
2351 case 0x6:
2352 case 0x7:
2353 case 0x8:
2354 case 0x9:
2355 case 0xA:
2356 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2357 return E_INVALIDARG;
2358
2359 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2360 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2361 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2362 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2363 break;
2364
2365 case 0x80000000:
2366 case 0x80000001:
2367 case 0x80000002:
2368 case 0x80000003:
2369 case 0x80000004:
2370 case 0x80000005:
2371 case 0x80000006:
2372 case 0x80000007:
2373 case 0x80000008:
2374 case 0x80000009:
2375 case 0x8000000A:
2376 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2377 return E_INVALIDARG;
2378
2379 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2380 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2381 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2382 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2383 break;
2384
2385 default:
2386 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2387 }
2388 return S_OK;
2389}
2390
2391STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2392{
2393 AutoCaller autoCaller(this);
2394 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2395
2396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2397
2398 HRESULT rc = checkStateDependency(MutableStateDep);
2399 if (FAILED(rc)) return rc;
2400
2401 switch(aId)
2402 {
2403 case 0x0:
2404 case 0x1:
2405 case 0x2:
2406 case 0x3:
2407 case 0x4:
2408 case 0x5:
2409 case 0x6:
2410 case 0x7:
2411 case 0x8:
2412 case 0x9:
2413 case 0xA:
2414 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2415 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2416 setModified(IsModified_MachineData);
2417 mHWData.backup();
2418 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2419 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2420 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2421 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2422 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2423 break;
2424
2425 case 0x80000000:
2426 case 0x80000001:
2427 case 0x80000002:
2428 case 0x80000003:
2429 case 0x80000004:
2430 case 0x80000005:
2431 case 0x80000006:
2432 case 0x80000007:
2433 case 0x80000008:
2434 case 0x80000009:
2435 case 0x8000000A:
2436 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2437 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2438 setModified(IsModified_MachineData);
2439 mHWData.backup();
2440 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2441 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2442 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2443 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2444 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2445 break;
2446
2447 default:
2448 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2449 }
2450 return S_OK;
2451}
2452
2453STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2454{
2455 AutoCaller autoCaller(this);
2456 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2457
2458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2459
2460 HRESULT rc = checkStateDependency(MutableStateDep);
2461 if (FAILED(rc)) return rc;
2462
2463 switch(aId)
2464 {
2465 case 0x0:
2466 case 0x1:
2467 case 0x2:
2468 case 0x3:
2469 case 0x4:
2470 case 0x5:
2471 case 0x6:
2472 case 0x7:
2473 case 0x8:
2474 case 0x9:
2475 case 0xA:
2476 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2477 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2478 setModified(IsModified_MachineData);
2479 mHWData.backup();
2480 /* Invalidate leaf. */
2481 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2482 break;
2483
2484 case 0x80000000:
2485 case 0x80000001:
2486 case 0x80000002:
2487 case 0x80000003:
2488 case 0x80000004:
2489 case 0x80000005:
2490 case 0x80000006:
2491 case 0x80000007:
2492 case 0x80000008:
2493 case 0x80000009:
2494 case 0x8000000A:
2495 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2496 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2497 setModified(IsModified_MachineData);
2498 mHWData.backup();
2499 /* Invalidate leaf. */
2500 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2501 break;
2502
2503 default:
2504 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2505 }
2506 return S_OK;
2507}
2508
2509STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2510{
2511 AutoCaller autoCaller(this);
2512 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2513
2514 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 HRESULT rc = checkStateDependency(MutableStateDep);
2517 if (FAILED(rc)) return rc;
2518
2519 setModified(IsModified_MachineData);
2520 mHWData.backup();
2521
2522 /* Invalidate all standard leafs. */
2523 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2524 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2525
2526 /* Invalidate all extended leafs. */
2527 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2528 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2529
2530 return S_OK;
2531}
2532
2533STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2534{
2535 CheckComArgOutPointerValid(aVal);
2536
2537 AutoCaller autoCaller(this);
2538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2539
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 switch(property)
2543 {
2544 case HWVirtExPropertyType_Enabled:
2545 *aVal = mHWData->mHWVirtExEnabled;
2546 break;
2547
2548 case HWVirtExPropertyType_Exclusive:
2549 *aVal = mHWData->mHWVirtExExclusive;
2550 break;
2551
2552 case HWVirtExPropertyType_VPID:
2553 *aVal = mHWData->mHWVirtExVPIDEnabled;
2554 break;
2555
2556 case HWVirtExPropertyType_NestedPaging:
2557 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2558 break;
2559
2560 case HWVirtExPropertyType_UnrestrictedExecution:
2561 *aVal = mHWData->mHWVirtExUXEnabled;
2562 break;
2563
2564 case HWVirtExPropertyType_LargePages:
2565 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2566#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2567 *aVal = FALSE;
2568#endif
2569 break;
2570
2571 case HWVirtExPropertyType_Force:
2572 *aVal = mHWData->mHWVirtExForceEnabled;
2573 break;
2574
2575 default:
2576 return E_INVALIDARG;
2577 }
2578 return S_OK;
2579}
2580
2581STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2582{
2583 AutoCaller autoCaller(this);
2584 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2585
2586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 HRESULT rc = checkStateDependency(MutableStateDep);
2589 if (FAILED(rc)) return rc;
2590
2591 switch(property)
2592 {
2593 case HWVirtExPropertyType_Enabled:
2594 setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mHWVirtExEnabled = !!aVal;
2597 break;
2598
2599 case HWVirtExPropertyType_Exclusive:
2600 setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mHWVirtExExclusive = !!aVal;
2603 break;
2604
2605 case HWVirtExPropertyType_VPID:
2606 setModified(IsModified_MachineData);
2607 mHWData.backup();
2608 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2609 break;
2610
2611 case HWVirtExPropertyType_NestedPaging:
2612 setModified(IsModified_MachineData);
2613 mHWData.backup();
2614 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2615 break;
2616
2617 case HWVirtExPropertyType_UnrestrictedExecution:
2618 setModified(IsModified_MachineData);
2619 mHWData.backup();
2620 mHWData->mHWVirtExUXEnabled = !!aVal;
2621 break;
2622
2623 case HWVirtExPropertyType_LargePages:
2624 setModified(IsModified_MachineData);
2625 mHWData.backup();
2626 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2627 break;
2628
2629 case HWVirtExPropertyType_Force:
2630 setModified(IsModified_MachineData);
2631 mHWData.backup();
2632 mHWData->mHWVirtExForceEnabled = !!aVal;
2633 break;
2634
2635 default:
2636 return E_INVALIDARG;
2637 }
2638
2639 return S_OK;
2640}
2641
2642STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2643{
2644 CheckComArgOutPointerValid(aSnapshotFolder);
2645
2646 AutoCaller autoCaller(this);
2647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2648
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 Utf8Str strFullSnapshotFolder;
2652 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2653 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2654
2655 return S_OK;
2656}
2657
2658STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2659{
2660 /* @todo (r=dmik):
2661 * 1. Allow to change the name of the snapshot folder containing snapshots
2662 * 2. Rename the folder on disk instead of just changing the property
2663 * value (to be smart and not to leave garbage). Note that it cannot be
2664 * done here because the change may be rolled back. Thus, the right
2665 * place is #saveSettings().
2666 */
2667
2668 AutoCaller autoCaller(this);
2669 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2670
2671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 HRESULT rc = checkStateDependency(MutableStateDep);
2674 if (FAILED(rc)) return rc;
2675
2676 if (!mData->mCurrentSnapshot.isNull())
2677 return setError(E_FAIL,
2678 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2679
2680 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2681
2682 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2683 if (strSnapshotFolder.isEmpty())
2684 strSnapshotFolder = "Snapshots";
2685 int vrc = calculateFullPath(strSnapshotFolder,
2686 strSnapshotFolder);
2687 if (RT_FAILURE(vrc))
2688 return setError(E_FAIL,
2689 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2690 aSnapshotFolder, vrc);
2691
2692 setModified(IsModified_MachineData);
2693 mUserData.backup();
2694
2695 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2696
2697 return S_OK;
2698}
2699
2700STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2701{
2702 CheckComArgOutSafeArrayPointerValid(aAttachments);
2703
2704 AutoCaller autoCaller(this);
2705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2706
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2710 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2711
2712 return S_OK;
2713}
2714
2715STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2716{
2717 CheckComArgOutPointerValid(vrdeServer);
2718
2719 AutoCaller autoCaller(this);
2720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2721
2722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2723
2724 Assert(!!mVRDEServer);
2725 mVRDEServer.queryInterfaceTo(vrdeServer);
2726
2727 return S_OK;
2728}
2729
2730STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2731{
2732 CheckComArgOutPointerValid(audioAdapter);
2733
2734 AutoCaller autoCaller(this);
2735 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2736
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 mAudioAdapter.queryInterfaceTo(audioAdapter);
2740 return S_OK;
2741}
2742
2743STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2744{
2745#ifdef VBOX_WITH_VUSB
2746 CheckComArgOutPointerValid(aUSBController);
2747
2748 AutoCaller autoCaller(this);
2749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2750
2751 clearError();
2752 MultiResult rc(S_OK);
2753
2754# ifdef VBOX_WITH_USB
2755 rc = mParent->host()->checkUSBProxyService();
2756 if (FAILED(rc)) return rc;
2757# endif
2758
2759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2760
2761 return rc = mUSBController.queryInterfaceTo(aUSBController);
2762#else
2763 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2764 * extended error info to indicate that USB is simply not available
2765 * (w/o treating it as a failure), for example, as in OSE */
2766 NOREF(aUSBController);
2767 ReturnComNotImplemented();
2768#endif /* VBOX_WITH_VUSB */
2769}
2770
2771STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters)
2772{
2773#ifdef VBOX_WITH_VUSB
2774 CheckComArgOutPointerValid(aUSBDeviceFilters);
2775
2776 AutoCaller autoCaller(this);
2777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2778
2779 clearError();
2780 MultiResult rc(S_OK);
2781
2782# ifdef VBOX_WITH_USB
2783 rc = mParent->host()->checkUSBProxyService();
2784 if (FAILED(rc)) return rc;
2785# endif
2786
2787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2788
2789 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters);
2790#else
2791 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2792 * extended error info to indicate that USB is simply not available
2793 * (w/o treating it as a failure), for example, as in OSE */
2794 NOREF(aUSBDeviceFilters);
2795 ReturnComNotImplemented();
2796#endif /* VBOX_WITH_VUSB */
2797}
2798
2799STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2800{
2801 CheckComArgOutPointerValid(aFilePath);
2802
2803 AutoLimitedCaller autoCaller(this);
2804 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2805
2806 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2807
2808 mData->m_strConfigFileFull.cloneTo(aFilePath);
2809 return S_OK;
2810}
2811
2812STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2813{
2814 CheckComArgOutPointerValid(aModified);
2815
2816 AutoCaller autoCaller(this);
2817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2818
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 HRESULT rc = checkStateDependency(MutableStateDep);
2822 if (FAILED(rc)) return rc;
2823
2824 if (!mData->pMachineConfigFile->fileExists())
2825 // this is a new machine, and no config file exists yet:
2826 *aModified = TRUE;
2827 else
2828 *aModified = (mData->flModifications != 0);
2829
2830 return S_OK;
2831}
2832
2833STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2834{
2835 CheckComArgOutPointerValid(aSessionState);
2836
2837 AutoCaller autoCaller(this);
2838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2839
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aSessionState = mData->mSession.mState;
2843
2844 return S_OK;
2845}
2846
2847STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2848{
2849 CheckComArgOutPointerValid(aSessionType);
2850
2851 AutoCaller autoCaller(this);
2852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2853
2854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2855
2856 mData->mSession.mType.cloneTo(aSessionType);
2857
2858 return S_OK;
2859}
2860
2861STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2862{
2863 CheckComArgOutPointerValid(aSessionPID);
2864
2865 AutoCaller autoCaller(this);
2866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2867
2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 *aSessionPID = mData->mSession.mPID;
2871
2872 return S_OK;
2873}
2874
2875STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2876{
2877 CheckComArgOutPointerValid(machineState);
2878
2879 AutoCaller autoCaller(this);
2880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2881
2882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 *machineState = mData->mMachineState;
2885
2886 return S_OK;
2887}
2888
2889STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2890{
2891 CheckComArgOutPointerValid(aLastStateChange);
2892
2893 AutoCaller autoCaller(this);
2894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2895
2896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2897
2898 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2899
2900 return S_OK;
2901}
2902
2903STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2904{
2905 CheckComArgOutPointerValid(aStateFilePath);
2906
2907 AutoCaller autoCaller(this);
2908 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2909
2910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2913
2914 return S_OK;
2915}
2916
2917STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2918{
2919 CheckComArgOutPointerValid(aLogFolder);
2920
2921 AutoCaller autoCaller(this);
2922 AssertComRCReturnRC(autoCaller.rc());
2923
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925
2926 Utf8Str logFolder;
2927 getLogFolder(logFolder);
2928 logFolder.cloneTo(aLogFolder);
2929
2930 return S_OK;
2931}
2932
2933STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2934{
2935 CheckComArgOutPointerValid(aCurrentSnapshot);
2936
2937 AutoCaller autoCaller(this);
2938 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2939
2940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2941
2942 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2943
2944 return S_OK;
2945}
2946
2947STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2948{
2949 CheckComArgOutPointerValid(aSnapshotCount);
2950
2951 AutoCaller autoCaller(this);
2952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2953
2954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2955
2956 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2957 ? 0
2958 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2959
2960 return S_OK;
2961}
2962
2963STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2964{
2965 CheckComArgOutPointerValid(aCurrentStateModified);
2966
2967 AutoCaller autoCaller(this);
2968 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2969
2970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2971
2972 /* Note: for machines with no snapshots, we always return FALSE
2973 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2974 * reasons :) */
2975
2976 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2977 ? FALSE
2978 : mData->mCurrentStateModified;
2979
2980 return S_OK;
2981}
2982
2983STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2984{
2985 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2986
2987 AutoCaller autoCaller(this);
2988 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2989
2990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2991
2992 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2993 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2994
2995 return S_OK;
2996}
2997
2998STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2999{
3000 CheckComArgOutPointerValid(aClipboardMode);
3001
3002 AutoCaller autoCaller(this);
3003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3004
3005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3006
3007 *aClipboardMode = mHWData->mClipboardMode;
3008
3009 return S_OK;
3010}
3011
3012STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
3013{
3014 HRESULT rc = S_OK;
3015
3016 AutoCaller autoCaller(this);
3017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3018
3019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3020
3021 alock.release();
3022 rc = onClipboardModeChange(aClipboardMode);
3023 alock.acquire();
3024 if (FAILED(rc)) return rc;
3025
3026 setModified(IsModified_MachineData);
3027 mHWData.backup();
3028 mHWData->mClipboardMode = aClipboardMode;
3029
3030 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3031 if (Global::IsOnline(mData->mMachineState))
3032 saveSettings(NULL);
3033
3034 return S_OK;
3035}
3036
3037STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
3038{
3039 CheckComArgOutPointerValid(aDragAndDropMode);
3040
3041 AutoCaller autoCaller(this);
3042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3043
3044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 *aDragAndDropMode = mHWData->mDragAndDropMode;
3047
3048 return S_OK;
3049}
3050
3051STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
3052{
3053 HRESULT rc = S_OK;
3054
3055 AutoCaller autoCaller(this);
3056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3057
3058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3059
3060 alock.release();
3061 rc = onDragAndDropModeChange(aDragAndDropMode);
3062 alock.acquire();
3063 if (FAILED(rc)) return rc;
3064
3065 setModified(IsModified_MachineData);
3066 mHWData.backup();
3067 mHWData->mDragAndDropMode = aDragAndDropMode;
3068
3069 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
3070 if (Global::IsOnline(mData->mMachineState))
3071 saveSettings(NULL);
3072
3073 return S_OK;
3074}
3075
3076STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
3077{
3078 CheckComArgOutPointerValid(aPatterns);
3079
3080 AutoCaller autoCaller(this);
3081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3082
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 try
3086 {
3087 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
3088 }
3089 catch (...)
3090 {
3091 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
3092 }
3093
3094 return S_OK;
3095}
3096
3097STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
3098{
3099 AutoCaller autoCaller(this);
3100 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3101
3102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3103
3104 HRESULT rc = checkStateDependency(MutableStateDep);
3105 if (FAILED(rc)) return rc;
3106
3107 setModified(IsModified_MachineData);
3108 mHWData.backup();
3109 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
3110 return rc;
3111}
3112
3113STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
3114{
3115 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
3116
3117 AutoCaller autoCaller(this);
3118 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3119
3120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3121
3122 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
3123 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
3124
3125 return S_OK;
3126}
3127
3128STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
3129{
3130 CheckComArgOutPointerValid(aEnabled);
3131
3132 AutoCaller autoCaller(this);
3133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3134
3135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3136
3137 *aEnabled = mUserData->s.fTeleporterEnabled;
3138
3139 return S_OK;
3140}
3141
3142STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
3143{
3144 AutoCaller autoCaller(this);
3145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3146
3147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3148
3149 /* Only allow it to be set to true when PoweredOff or Aborted.
3150 (Clearing it is always permitted.) */
3151 if ( aEnabled
3152 && mData->mRegistered
3153 && ( !isSessionMachine()
3154 || ( mData->mMachineState != MachineState_PoweredOff
3155 && mData->mMachineState != MachineState_Teleported
3156 && mData->mMachineState != MachineState_Aborted
3157 )
3158 )
3159 )
3160 return setError(VBOX_E_INVALID_VM_STATE,
3161 tr("The machine is not powered off (state is %s)"),
3162 Global::stringifyMachineState(mData->mMachineState));
3163
3164 setModified(IsModified_MachineData);
3165 mUserData.backup();
3166 mUserData->s.fTeleporterEnabled = !!aEnabled;
3167
3168 return S_OK;
3169}
3170
3171STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
3172{
3173 CheckComArgOutPointerValid(aPort);
3174
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3177
3178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 *aPort = (ULONG)mUserData->s.uTeleporterPort;
3181
3182 return S_OK;
3183}
3184
3185STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
3186{
3187 if (aPort >= _64K)
3188 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
3189
3190 AutoCaller autoCaller(this);
3191 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3192
3193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3194
3195 HRESULT rc = checkStateDependency(MutableStateDep);
3196 if (FAILED(rc)) return rc;
3197
3198 setModified(IsModified_MachineData);
3199 mUserData.backup();
3200 mUserData->s.uTeleporterPort = (uint32_t)aPort;
3201
3202 return S_OK;
3203}
3204
3205STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3206{
3207 CheckComArgOutPointerValid(aAddress);
3208
3209 AutoCaller autoCaller(this);
3210 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3211
3212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3215
3216 return S_OK;
3217}
3218
3219STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3220{
3221 AutoCaller autoCaller(this);
3222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3223
3224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3225
3226 HRESULT rc = checkStateDependency(MutableStateDep);
3227 if (FAILED(rc)) return rc;
3228
3229 setModified(IsModified_MachineData);
3230 mUserData.backup();
3231 mUserData->s.strTeleporterAddress = aAddress;
3232
3233 return S_OK;
3234}
3235
3236STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3237{
3238 CheckComArgOutPointerValid(aPassword);
3239
3240 AutoCaller autoCaller(this);
3241 HRESULT hrc = autoCaller.rc();
3242 if (SUCCEEDED(hrc))
3243 {
3244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3245 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3246 }
3247
3248 return hrc;
3249}
3250
3251STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3252{
3253 /*
3254 * Hash the password first.
3255 */
3256 Utf8Str strPassword(aPassword);
3257 if (!strPassword.isEmpty())
3258 {
3259 if (VBoxIsPasswordHashed(&strPassword))
3260 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3261 VBoxHashPassword(&strPassword);
3262 }
3263
3264 /*
3265 * Do the update.
3266 */
3267 AutoCaller autoCaller(this);
3268 HRESULT hrc = autoCaller.rc();
3269 if (SUCCEEDED(hrc))
3270 {
3271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3272 hrc = checkStateDependency(MutableStateDep);
3273 if (SUCCEEDED(hrc))
3274 {
3275 setModified(IsModified_MachineData);
3276 mUserData.backup();
3277 mUserData->s.strTeleporterPassword = strPassword;
3278 }
3279 }
3280
3281 return hrc;
3282}
3283
3284STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3285{
3286 CheckComArgOutPointerValid(aState);
3287
3288 AutoCaller autoCaller(this);
3289 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3290
3291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3292
3293 *aState = mUserData->s.enmFaultToleranceState;
3294 return S_OK;
3295}
3296
3297STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3298{
3299 AutoCaller autoCaller(this);
3300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3301
3302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3303
3304 /* @todo deal with running state change. */
3305 HRESULT rc = checkStateDependency(MutableStateDep);
3306 if (FAILED(rc)) return rc;
3307
3308 setModified(IsModified_MachineData);
3309 mUserData.backup();
3310 mUserData->s.enmFaultToleranceState = aState;
3311 return S_OK;
3312}
3313
3314STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3315{
3316 CheckComArgOutPointerValid(aAddress);
3317
3318 AutoCaller autoCaller(this);
3319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3320
3321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3322
3323 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3324 return S_OK;
3325}
3326
3327STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3328{
3329 AutoCaller autoCaller(this);
3330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3331
3332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3333
3334 /* @todo deal with running state change. */
3335 HRESULT rc = checkStateDependency(MutableStateDep);
3336 if (FAILED(rc)) return rc;
3337
3338 setModified(IsModified_MachineData);
3339 mUserData.backup();
3340 mUserData->s.strFaultToleranceAddress = aAddress;
3341 return S_OK;
3342}
3343
3344STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3345{
3346 CheckComArgOutPointerValid(aPort);
3347
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3352
3353 *aPort = mUserData->s.uFaultTolerancePort;
3354 return S_OK;
3355}
3356
3357STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3358{
3359 AutoCaller autoCaller(this);
3360 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3361
3362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3363
3364 /* @todo deal with running state change. */
3365 HRESULT rc = checkStateDependency(MutableStateDep);
3366 if (FAILED(rc)) return rc;
3367
3368 setModified(IsModified_MachineData);
3369 mUserData.backup();
3370 mUserData->s.uFaultTolerancePort = aPort;
3371 return S_OK;
3372}
3373
3374STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3375{
3376 CheckComArgOutPointerValid(aPassword);
3377
3378 AutoCaller autoCaller(this);
3379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3380
3381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3382
3383 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3384
3385 return S_OK;
3386}
3387
3388STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3389{
3390 AutoCaller autoCaller(this);
3391 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3392
3393 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3394
3395 /* @todo deal with running state change. */
3396 HRESULT rc = checkStateDependency(MutableStateDep);
3397 if (FAILED(rc)) return rc;
3398
3399 setModified(IsModified_MachineData);
3400 mUserData.backup();
3401 mUserData->s.strFaultTolerancePassword = aPassword;
3402
3403 return S_OK;
3404}
3405
3406STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3407{
3408 CheckComArgOutPointerValid(aInterval);
3409
3410 AutoCaller autoCaller(this);
3411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3412
3413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3414
3415 *aInterval = mUserData->s.uFaultToleranceInterval;
3416 return S_OK;
3417}
3418
3419STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3420{
3421 AutoCaller autoCaller(this);
3422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3423
3424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3425
3426 /* @todo deal with running state change. */
3427 HRESULT rc = checkStateDependency(MutableStateDep);
3428 if (FAILED(rc)) return rc;
3429
3430 setModified(IsModified_MachineData);
3431 mUserData.backup();
3432 mUserData->s.uFaultToleranceInterval = aInterval;
3433 return S_OK;
3434}
3435
3436STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3437{
3438 CheckComArgOutPointerValid(aEnabled);
3439
3440 AutoCaller autoCaller(this);
3441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3442
3443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3444
3445 *aEnabled = mUserData->s.fRTCUseUTC;
3446
3447 return S_OK;
3448}
3449
3450STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3451{
3452 AutoCaller autoCaller(this);
3453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3454
3455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3456
3457 /* Only allow it to be set to true when PoweredOff or Aborted.
3458 (Clearing it is always permitted.) */
3459 if ( aEnabled
3460 && mData->mRegistered
3461 && ( !isSessionMachine()
3462 || ( mData->mMachineState != MachineState_PoweredOff
3463 && mData->mMachineState != MachineState_Teleported
3464 && mData->mMachineState != MachineState_Aborted
3465 )
3466 )
3467 )
3468 return setError(VBOX_E_INVALID_VM_STATE,
3469 tr("The machine is not powered off (state is %s)"),
3470 Global::stringifyMachineState(mData->mMachineState));
3471
3472 setModified(IsModified_MachineData);
3473 mUserData.backup();
3474 mUserData->s.fRTCUseUTC = !!aEnabled;
3475
3476 return S_OK;
3477}
3478
3479STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3480{
3481 CheckComArgOutPointerValid(aEnabled);
3482
3483 AutoCaller autoCaller(this);
3484 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3485
3486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3487
3488 *aEnabled = mHWData->mIOCacheEnabled;
3489
3490 return S_OK;
3491}
3492
3493STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3494{
3495 AutoCaller autoCaller(this);
3496 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3497
3498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3499
3500 HRESULT rc = checkStateDependency(MutableStateDep);
3501 if (FAILED(rc)) return rc;
3502
3503 setModified(IsModified_MachineData);
3504 mHWData.backup();
3505 mHWData->mIOCacheEnabled = aEnabled;
3506
3507 return S_OK;
3508}
3509
3510STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3511{
3512 CheckComArgOutPointerValid(aIOCacheSize);
3513
3514 AutoCaller autoCaller(this);
3515 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3516
3517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3518
3519 *aIOCacheSize = mHWData->mIOCacheSize;
3520
3521 return S_OK;
3522}
3523
3524STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3525{
3526 AutoCaller autoCaller(this);
3527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3528
3529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3530
3531 HRESULT rc = checkStateDependency(MutableStateDep);
3532 if (FAILED(rc)) return rc;
3533
3534 setModified(IsModified_MachineData);
3535 mHWData.backup();
3536 mHWData->mIOCacheSize = aIOCacheSize;
3537
3538 return S_OK;
3539}
3540
3541
3542/**
3543 * @note Locks objects!
3544 */
3545STDMETHODIMP Machine::LockMachine(ISession *aSession,
3546 LockType_T lockType)
3547{
3548 CheckComArgNotNull(aSession);
3549
3550 AutoCaller autoCaller(this);
3551 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3552
3553 /* check the session state */
3554 SessionState_T state;
3555 HRESULT rc = aSession->COMGETTER(State)(&state);
3556 if (FAILED(rc)) return rc;
3557
3558 if (state != SessionState_Unlocked)
3559 return setError(VBOX_E_INVALID_OBJECT_STATE,
3560 tr("The given session is busy"));
3561
3562 // get the client's IInternalSessionControl interface
3563 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3564 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3565 E_INVALIDARG);
3566
3567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3568
3569 if (!mData->mRegistered)
3570 return setError(E_UNEXPECTED,
3571 tr("The machine '%s' is not registered"),
3572 mUserData->s.strName.c_str());
3573
3574 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3575
3576 SessionState_T oldState = mData->mSession.mState;
3577 /* Hack: in case the session is closing and there is a progress object
3578 * which allows waiting for the session to be closed, take the opportunity
3579 * and do a limited wait (max. 1 second). This helps a lot when the system
3580 * is busy and thus session closing can take a little while. */
3581 if ( mData->mSession.mState == SessionState_Unlocking
3582 && mData->mSession.mProgress)
3583 {
3584 alock.release();
3585 mData->mSession.mProgress->WaitForCompletion(1000);
3586 alock.acquire();
3587 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3588 }
3589
3590 // try again now
3591 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3592 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3593 )
3594 {
3595 // OK, share the session... we are now dealing with three processes:
3596 // 1) VBoxSVC (where this code runs);
3597 // 2) process C: the caller's client process (who wants a shared session);
3598 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3599
3600 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3601 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3602 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3603 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3604 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3605
3606 /*
3607 * Release the lock before calling the client process. It's safe here
3608 * since the only thing to do after we get the lock again is to add
3609 * the remote control to the list (which doesn't directly influence
3610 * anything).
3611 */
3612 alock.release();
3613
3614 // get the console of the session holding the write lock (this is a remote call)
3615 ComPtr<IConsole> pConsoleW;
3616 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3617 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3618 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3619 if (FAILED(rc))
3620 // the failure may occur w/o any error info (from RPC), so provide one
3621 return setError(VBOX_E_VM_ERROR,
3622 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3623
3624 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3625
3626 // share the session machine and W's console with the caller's session
3627 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3628 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3629 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3630
3631 if (FAILED(rc))
3632 // the failure may occur w/o any error info (from RPC), so provide one
3633 return setError(VBOX_E_VM_ERROR,
3634 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3635 alock.acquire();
3636
3637 // need to revalidate the state after acquiring the lock again
3638 if (mData->mSession.mState != SessionState_Locked)
3639 {
3640 pSessionControl->Uninitialize();
3641 return setError(VBOX_E_INVALID_SESSION_STATE,
3642 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3643 mUserData->s.strName.c_str());
3644 }
3645
3646 // add the caller's session to the list
3647 mData->mSession.mRemoteControls.push_back(pSessionControl);
3648 }
3649 else if ( mData->mSession.mState == SessionState_Locked
3650 || mData->mSession.mState == SessionState_Unlocking
3651 )
3652 {
3653 // sharing not permitted, or machine still unlocking:
3654 return setError(VBOX_E_INVALID_OBJECT_STATE,
3655 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3656 mUserData->s.strName.c_str());
3657 }
3658 else
3659 {
3660 // machine is not locked: then write-lock the machine (create the session machine)
3661
3662 // must not be busy
3663 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3664
3665 // get the caller's session PID
3666 RTPROCESS pid = NIL_RTPROCESS;
3667 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3668 pSessionControl->GetPID((ULONG*)&pid);
3669 Assert(pid != NIL_RTPROCESS);
3670
3671 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3672
3673 if (fLaunchingVMProcess)
3674 {
3675 // this machine is awaiting for a spawning session to be opened:
3676 // then the calling process must be the one that got started by
3677 // LaunchVMProcess()
3678
3679 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3680 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3681
3682 if (mData->mSession.mPID != pid)
3683 return setError(E_ACCESSDENIED,
3684 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3685 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3686 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3687 }
3688
3689 // create the mutable SessionMachine from the current machine
3690 ComObjPtr<SessionMachine> sessionMachine;
3691 sessionMachine.createObject();
3692 rc = sessionMachine->init(this);
3693 AssertComRC(rc);
3694
3695 /* NOTE: doing return from this function after this point but
3696 * before the end is forbidden since it may call SessionMachine::uninit()
3697 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3698 * lock while still holding the Machine lock in alock so that a deadlock
3699 * is possible due to the wrong lock order. */
3700
3701 if (SUCCEEDED(rc))
3702 {
3703 /*
3704 * Set the session state to Spawning to protect against subsequent
3705 * attempts to open a session and to unregister the machine after
3706 * we release the lock.
3707 */
3708 SessionState_T origState = mData->mSession.mState;
3709 mData->mSession.mState = SessionState_Spawning;
3710
3711 /*
3712 * Release the lock before calling the client process -- it will call
3713 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3714 * because the state is Spawning, so that LaunchVMProcess() and
3715 * LockMachine() calls will fail. This method, called before we
3716 * acquire the lock again, will fail because of the wrong PID.
3717 *
3718 * Note that mData->mSession.mRemoteControls accessed outside
3719 * the lock may not be modified when state is Spawning, so it's safe.
3720 */
3721 alock.release();
3722
3723 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3724 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3725 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3726
3727 /* The failure may occur w/o any error info (from RPC), so provide one */
3728 if (FAILED(rc))
3729 setError(VBOX_E_VM_ERROR,
3730 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3731
3732 if ( SUCCEEDED(rc)
3733 && fLaunchingVMProcess
3734 )
3735 {
3736 /* complete the remote session initialization */
3737
3738 /* get the console from the direct session */
3739 ComPtr<IConsole> console;
3740 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3741 ComAssertComRC(rc);
3742
3743 if (SUCCEEDED(rc) && !console)
3744 {
3745 ComAssert(!!console);
3746 rc = E_FAIL;
3747 }
3748
3749 /* assign machine & console to the remote session */
3750 if (SUCCEEDED(rc))
3751 {
3752 /*
3753 * after LaunchVMProcess(), the first and the only
3754 * entry in remoteControls is that remote session
3755 */
3756 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3757 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3758 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3759
3760 /* The failure may occur w/o any error info (from RPC), so provide one */
3761 if (FAILED(rc))
3762 setError(VBOX_E_VM_ERROR,
3763 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3764 }
3765
3766 if (FAILED(rc))
3767 pSessionControl->Uninitialize();
3768 }
3769
3770 /* acquire the lock again */
3771 alock.acquire();
3772
3773 /* Restore the session state */
3774 mData->mSession.mState = origState;
3775 }
3776
3777 // finalize spawning anyway (this is why we don't return on errors above)
3778 if (fLaunchingVMProcess)
3779 {
3780 /* Note that the progress object is finalized later */
3781 /** @todo Consider checking mData->mSession.mProgress for cancellation
3782 * around here. */
3783
3784 /* We don't reset mSession.mPID here because it is necessary for
3785 * SessionMachine::uninit() to reap the child process later. */
3786
3787 if (FAILED(rc))
3788 {
3789 /* Close the remote session, remove the remote control from the list
3790 * and reset session state to Closed (@note keep the code in sync
3791 * with the relevant part in openSession()). */
3792
3793 Assert(mData->mSession.mRemoteControls.size() == 1);
3794 if (mData->mSession.mRemoteControls.size() == 1)
3795 {
3796 ErrorInfoKeeper eik;
3797 mData->mSession.mRemoteControls.front()->Uninitialize();
3798 }
3799
3800 mData->mSession.mRemoteControls.clear();
3801 mData->mSession.mState = SessionState_Unlocked;
3802 }
3803 }
3804 else
3805 {
3806 /* memorize PID of the directly opened session */
3807 if (SUCCEEDED(rc))
3808 mData->mSession.mPID = pid;
3809 }
3810
3811 if (SUCCEEDED(rc))
3812 {
3813 /* memorize the direct session control and cache IUnknown for it */
3814 mData->mSession.mDirectControl = pSessionControl;
3815 mData->mSession.mState = SessionState_Locked;
3816 /* associate the SessionMachine with this Machine */
3817 mData->mSession.mMachine = sessionMachine;
3818
3819 /* request an IUnknown pointer early from the remote party for later
3820 * identity checks (it will be internally cached within mDirectControl
3821 * at least on XPCOM) */
3822 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3823 NOREF(unk);
3824 }
3825
3826 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3827 * would break the lock order */
3828 alock.release();
3829
3830 /* uninitialize the created session machine on failure */
3831 if (FAILED(rc))
3832 sessionMachine->uninit();
3833
3834 }
3835
3836 if (SUCCEEDED(rc))
3837 {
3838 /*
3839 * tell the client watcher thread to update the set of
3840 * machines that have open sessions
3841 */
3842 mParent->updateClientWatcher();
3843
3844 if (oldState != SessionState_Locked)
3845 /* fire an event */
3846 mParent->onSessionStateChange(getId(), SessionState_Locked);
3847 }
3848
3849 return rc;
3850}
3851
3852/**
3853 * @note Locks objects!
3854 */
3855STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3856 IN_BSTR aFrontend,
3857 IN_BSTR aEnvironment,
3858 IProgress **aProgress)
3859{
3860 CheckComArgStr(aFrontend);
3861 Utf8Str strFrontend(aFrontend);
3862 Utf8Str strEnvironment(aEnvironment);
3863 /* "emergencystop" doesn't need the session, so skip the checks/interface
3864 * retrieval. This code doesn't quite fit in here, but introducing a
3865 * special API method would be even more effort, and would require explicit
3866 * support by every API client. It's better to hide the feature a bit. */
3867 if (strFrontend != "emergencystop")
3868 CheckComArgNotNull(aSession);
3869 CheckComArgOutPointerValid(aProgress);
3870
3871 AutoCaller autoCaller(this);
3872 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3873
3874 HRESULT rc = S_OK;
3875 if (strFrontend.isEmpty())
3876 {
3877 Bstr bstrFrontend;
3878 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3879 if (FAILED(rc))
3880 return rc;
3881 strFrontend = bstrFrontend;
3882 if (strFrontend.isEmpty())
3883 {
3884 ComPtr<ISystemProperties> systemProperties;
3885 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3886 if (FAILED(rc))
3887 return rc;
3888 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3889 if (FAILED(rc))
3890 return rc;
3891 strFrontend = bstrFrontend;
3892 }
3893 /* paranoia - emergencystop is not a valid default */
3894 if (strFrontend == "emergencystop")
3895 strFrontend = Utf8Str::Empty;
3896 }
3897 /* default frontend: Qt GUI */
3898 if (strFrontend.isEmpty())
3899 strFrontend = "GUI/Qt";
3900
3901 if (strFrontend != "emergencystop")
3902 {
3903 /* check the session state */
3904 SessionState_T state;
3905 rc = aSession->COMGETTER(State)(&state);
3906 if (FAILED(rc))
3907 return rc;
3908
3909 if (state != SessionState_Unlocked)
3910 return setError(VBOX_E_INVALID_OBJECT_STATE,
3911 tr("The given session is busy"));
3912
3913 /* get the IInternalSessionControl interface */
3914 ComPtr<IInternalSessionControl> control(aSession);
3915 ComAssertMsgRet(!control.isNull(),
3916 ("No IInternalSessionControl interface"),
3917 E_INVALIDARG);
3918
3919 /* get the teleporter enable state for the progress object init. */
3920 BOOL fTeleporterEnabled;
3921 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3922 if (FAILED(rc))
3923 return rc;
3924
3925 /* create a progress object */
3926 ComObjPtr<ProgressProxy> progress;
3927 progress.createObject();
3928 rc = progress->init(mParent,
3929 static_cast<IMachine*>(this),
3930 Bstr(tr("Starting VM")).raw(),
3931 TRUE /* aCancelable */,
3932 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3933 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3934 2 /* uFirstOperationWeight */,
3935 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3936
3937 if (SUCCEEDED(rc))
3938 {
3939 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3940 if (SUCCEEDED(rc))
3941 {
3942 progress.queryInterfaceTo(aProgress);
3943
3944 /* signal the client watcher thread */
3945 mParent->updateClientWatcher();
3946
3947 /* fire an event */
3948 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3949 }
3950 }
3951 }
3952 else
3953 {
3954 /* no progress object - either instant success or failure */
3955 *aProgress = NULL;
3956
3957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3958
3959 if (mData->mSession.mState != SessionState_Locked)
3960 return setError(VBOX_E_INVALID_OBJECT_STATE,
3961 tr("The machine '%s' is not locked by a session"),
3962 mUserData->s.strName.c_str());
3963
3964 /* must have a VM process associated - do not kill normal API clients
3965 * with an open session */
3966 if (!Global::IsOnline(mData->mMachineState))
3967 return setError(VBOX_E_INVALID_OBJECT_STATE,
3968 tr("The machine '%s' does not have a VM process"),
3969 mUserData->s.strName.c_str());
3970
3971 /* forcibly terminate the VM process */
3972 if (mData->mSession.mPID != NIL_RTPROCESS)
3973 RTProcTerminate(mData->mSession.mPID);
3974
3975 /* signal the client watcher thread, as most likely the client has
3976 * been terminated */
3977 mParent->updateClientWatcher();
3978 }
3979
3980 return rc;
3981}
3982
3983STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3984{
3985 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3986 return setError(E_INVALIDARG,
3987 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3988 aPosition, SchemaDefs::MaxBootPosition);
3989
3990 if (aDevice == DeviceType_USB)
3991 return setError(E_NOTIMPL,
3992 tr("Booting from USB device is currently not supported"));
3993
3994 AutoCaller autoCaller(this);
3995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3996
3997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3998
3999 HRESULT rc = checkStateDependency(MutableStateDep);
4000 if (FAILED(rc)) return rc;
4001
4002 setModified(IsModified_MachineData);
4003 mHWData.backup();
4004 mHWData->mBootOrder[aPosition - 1] = aDevice;
4005
4006 return S_OK;
4007}
4008
4009STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
4010{
4011 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
4012 return setError(E_INVALIDARG,
4013 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
4014 aPosition, SchemaDefs::MaxBootPosition);
4015
4016 AutoCaller autoCaller(this);
4017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4018
4019 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4020
4021 *aDevice = mHWData->mBootOrder[aPosition - 1];
4022
4023 return S_OK;
4024}
4025
4026STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
4027 LONG aControllerPort,
4028 LONG aDevice,
4029 DeviceType_T aType,
4030 IMedium *aMedium)
4031{
4032 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4033 aControllerName, aControllerPort, aDevice, aType, aMedium));
4034
4035 CheckComArgStrNotEmptyOrNull(aControllerName);
4036
4037 AutoCaller autoCaller(this);
4038 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4039
4040 // request the host lock first, since might be calling Host methods for getting host drives;
4041 // next, protect the media tree all the while we're in here, as well as our member variables
4042 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
4043 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4044
4045 HRESULT rc = checkStateDependency(MutableStateDep);
4046 if (FAILED(rc)) return rc;
4047
4048 /// @todo NEWMEDIA implicit machine registration
4049 if (!mData->mRegistered)
4050 return setError(VBOX_E_INVALID_OBJECT_STATE,
4051 tr("Cannot attach storage devices to an unregistered machine"));
4052
4053 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4054
4055 /* Check for an existing controller. */
4056 ComObjPtr<StorageController> ctl;
4057 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4058 if (FAILED(rc)) return rc;
4059
4060 StorageControllerType_T ctrlType;
4061 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4062 if (FAILED(rc))
4063 return setError(E_FAIL,
4064 tr("Could not get type of controller '%ls'"),
4065 aControllerName);
4066
4067 bool fSilent = false;
4068 Utf8Str strReconfig;
4069
4070 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4071 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4072 if (FAILED(rc))
4073 return rc;
4074 if ( mData->mMachineState == MachineState_Paused
4075 && strReconfig == "1")
4076 fSilent = true;
4077
4078 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4079 bool fHotplug = false;
4080 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4081 fHotplug = true;
4082
4083 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4084 return setError(VBOX_E_INVALID_VM_STATE,
4085 tr("Controller '%ls' does not support hotplugging"),
4086 aControllerName);
4087
4088 // check that the port and device are not out of range
4089 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
4090 if (FAILED(rc)) return rc;
4091
4092 /* check if the device slot is already busy */
4093 MediumAttachment *pAttachTemp;
4094 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
4095 aControllerName,
4096 aControllerPort,
4097 aDevice)))
4098 {
4099 Medium *pMedium = pAttachTemp->getMedium();
4100 if (pMedium)
4101 {
4102 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4103 return setError(VBOX_E_OBJECT_IN_USE,
4104 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4105 pMedium->getLocationFull().c_str(),
4106 aControllerPort,
4107 aDevice,
4108 aControllerName);
4109 }
4110 else
4111 return setError(VBOX_E_OBJECT_IN_USE,
4112 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
4113 aControllerPort, aDevice, aControllerName);
4114 }
4115
4116 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
4117 if (aMedium && medium.isNull())
4118 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4119
4120 AutoCaller mediumCaller(medium);
4121 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4122
4123 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
4124
4125 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
4126 && !medium.isNull()
4127 )
4128 return setError(VBOX_E_OBJECT_IN_USE,
4129 tr("Medium '%s' is already attached to this virtual machine"),
4130 medium->getLocationFull().c_str());
4131
4132 if (!medium.isNull())
4133 {
4134 MediumType_T mtype = medium->getType();
4135 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
4136 // For DVDs it's not written to the config file, so needs no global config
4137 // version bump. For floppies it's a new attribute "type", which is ignored
4138 // by older VirtualBox version, so needs no global config version bump either.
4139 // For hard disks this type is not accepted.
4140 if (mtype == MediumType_MultiAttach)
4141 {
4142 // This type is new with VirtualBox 4.0 and therefore requires settings
4143 // version 1.11 in the settings backend. Unfortunately it is not enough to do
4144 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
4145 // two reasons: The medium type is a property of the media registry tree, which
4146 // can reside in the global config file (for pre-4.0 media); we would therefore
4147 // possibly need to bump the global config version. We don't want to do that though
4148 // because that might make downgrading to pre-4.0 impossible.
4149 // As a result, we can only use these two new types if the medium is NOT in the
4150 // global registry:
4151 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
4152 if ( medium->isInRegistry(uuidGlobalRegistry)
4153 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
4154 )
4155 return setError(VBOX_E_INVALID_OBJECT_STATE,
4156 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
4157 "to machines that were created with VirtualBox 4.0 or later"),
4158 medium->getLocationFull().c_str());
4159 }
4160 }
4161
4162 bool fIndirect = false;
4163 if (!medium.isNull())
4164 fIndirect = medium->isReadOnly();
4165 bool associate = true;
4166
4167 do
4168 {
4169 if ( aType == DeviceType_HardDisk
4170 && mMediaData.isBackedUp())
4171 {
4172 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4173
4174 /* check if the medium was attached to the VM before we started
4175 * changing attachments in which case the attachment just needs to
4176 * be restored */
4177 if ((pAttachTemp = findAttachment(oldAtts, medium)))
4178 {
4179 AssertReturn(!fIndirect, E_FAIL);
4180
4181 /* see if it's the same bus/channel/device */
4182 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
4183 {
4184 /* the simplest case: restore the whole attachment
4185 * and return, nothing else to do */
4186 mMediaData->mAttachments.push_back(pAttachTemp);
4187
4188 /* Reattach the medium to the VM. */
4189 if (fHotplug || fSilent)
4190 {
4191 mediumLock.release();
4192 treeLock.release();
4193 alock.release();
4194
4195 MediumLockList *pMediumLockList(new MediumLockList());
4196
4197 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4198 true /* fMediumLockWrite */,
4199 NULL,
4200 *pMediumLockList);
4201 alock.acquire();
4202 if (FAILED(rc))
4203 delete pMediumLockList;
4204 else
4205 {
4206 mData->mSession.mLockedMedia.Unlock();
4207 alock.release();
4208 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4209 mData->mSession.mLockedMedia.Lock();
4210 alock.acquire();
4211 }
4212 alock.release();
4213
4214 if (SUCCEEDED(rc))
4215 {
4216 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4217 /* Remove lock list in case of error. */
4218 if (FAILED(rc))
4219 {
4220 mData->mSession.mLockedMedia.Unlock();
4221 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4222 mData->mSession.mLockedMedia.Lock();
4223 }
4224 }
4225 }
4226
4227 return S_OK;
4228 }
4229
4230 /* bus/channel/device differ; we need a new attachment object,
4231 * but don't try to associate it again */
4232 associate = false;
4233 break;
4234 }
4235 }
4236
4237 /* go further only if the attachment is to be indirect */
4238 if (!fIndirect)
4239 break;
4240
4241 /* perform the so called smart attachment logic for indirect
4242 * attachments. Note that smart attachment is only applicable to base
4243 * hard disks. */
4244
4245 if (medium->getParent().isNull())
4246 {
4247 /* first, investigate the backup copy of the current hard disk
4248 * attachments to make it possible to re-attach existing diffs to
4249 * another device slot w/o losing their contents */
4250 if (mMediaData.isBackedUp())
4251 {
4252 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4253
4254 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4255 uint32_t foundLevel = 0;
4256
4257 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4258 it != oldAtts.end();
4259 ++it)
4260 {
4261 uint32_t level = 0;
4262 MediumAttachment *pAttach = *it;
4263 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4264 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4265 if (pMedium.isNull())
4266 continue;
4267
4268 if (pMedium->getBase(&level) == medium)
4269 {
4270 /* skip the hard disk if its currently attached (we
4271 * cannot attach the same hard disk twice) */
4272 if (findAttachment(mMediaData->mAttachments,
4273 pMedium))
4274 continue;
4275
4276 /* matched device, channel and bus (i.e. attached to the
4277 * same place) will win and immediately stop the search;
4278 * otherwise the attachment that has the youngest
4279 * descendant of medium will be used
4280 */
4281 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4282 {
4283 /* the simplest case: restore the whole attachment
4284 * and return, nothing else to do */
4285 mMediaData->mAttachments.push_back(*it);
4286
4287 /* Reattach the medium to the VM. */
4288 if (fHotplug || fSilent)
4289 {
4290 mediumLock.release();
4291 treeLock.release();
4292 alock.release();
4293
4294 MediumLockList *pMediumLockList(new MediumLockList());
4295
4296 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4297 true /* fMediumLockWrite */,
4298 NULL,
4299 *pMediumLockList);
4300 alock.acquire();
4301 if (FAILED(rc))
4302 delete pMediumLockList;
4303 else
4304 {
4305 mData->mSession.mLockedMedia.Unlock();
4306 alock.release();
4307 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4308 mData->mSession.mLockedMedia.Lock();
4309 alock.acquire();
4310 }
4311 alock.release();
4312
4313 if (SUCCEEDED(rc))
4314 {
4315 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4316 /* Remove lock list in case of error. */
4317 if (FAILED(rc))
4318 {
4319 mData->mSession.mLockedMedia.Unlock();
4320 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4321 mData->mSession.mLockedMedia.Lock();
4322 }
4323 }
4324 }
4325
4326 return S_OK;
4327 }
4328 else if ( foundIt == oldAtts.end()
4329 || level > foundLevel /* prefer younger */
4330 )
4331 {
4332 foundIt = it;
4333 foundLevel = level;
4334 }
4335 }
4336 }
4337
4338 if (foundIt != oldAtts.end())
4339 {
4340 /* use the previously attached hard disk */
4341 medium = (*foundIt)->getMedium();
4342 mediumCaller.attach(medium);
4343 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4344 mediumLock.attach(medium);
4345 /* not implicit, doesn't require association with this VM */
4346 fIndirect = false;
4347 associate = false;
4348 /* go right to the MediumAttachment creation */
4349 break;
4350 }
4351 }
4352
4353 /* must give up the medium lock and medium tree lock as below we
4354 * go over snapshots, which needs a lock with higher lock order. */
4355 mediumLock.release();
4356 treeLock.release();
4357
4358 /* then, search through snapshots for the best diff in the given
4359 * hard disk's chain to base the new diff on */
4360
4361 ComObjPtr<Medium> base;
4362 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4363 while (snap)
4364 {
4365 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4366
4367 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4368
4369 MediumAttachment *pAttachFound = NULL;
4370 uint32_t foundLevel = 0;
4371
4372 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4373 it != snapAtts.end();
4374 ++it)
4375 {
4376 MediumAttachment *pAttach = *it;
4377 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4378 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4379 if (pMedium.isNull())
4380 continue;
4381
4382 uint32_t level = 0;
4383 if (pMedium->getBase(&level) == medium)
4384 {
4385 /* matched device, channel and bus (i.e. attached to the
4386 * same place) will win and immediately stop the search;
4387 * otherwise the attachment that has the youngest
4388 * descendant of medium will be used
4389 */
4390 if ( pAttach->getDevice() == aDevice
4391 && pAttach->getPort() == aControllerPort
4392 && pAttach->getControllerName() == aControllerName
4393 )
4394 {
4395 pAttachFound = pAttach;
4396 break;
4397 }
4398 else if ( !pAttachFound
4399 || level > foundLevel /* prefer younger */
4400 )
4401 {
4402 pAttachFound = pAttach;
4403 foundLevel = level;
4404 }
4405 }
4406 }
4407
4408 if (pAttachFound)
4409 {
4410 base = pAttachFound->getMedium();
4411 break;
4412 }
4413
4414 snap = snap->getParent();
4415 }
4416
4417 /* re-lock medium tree and the medium, as we need it below */
4418 treeLock.acquire();
4419 mediumLock.acquire();
4420
4421 /* found a suitable diff, use it as a base */
4422 if (!base.isNull())
4423 {
4424 medium = base;
4425 mediumCaller.attach(medium);
4426 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4427 mediumLock.attach(medium);
4428 }
4429 }
4430
4431 Utf8Str strFullSnapshotFolder;
4432 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4433
4434 ComObjPtr<Medium> diff;
4435 diff.createObject();
4436 // store this diff in the same registry as the parent
4437 Guid uuidRegistryParent;
4438 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4439 {
4440 // parent image has no registry: this can happen if we're attaching a new immutable
4441 // image that has not yet been attached (medium then points to the base and we're
4442 // creating the diff image for the immutable, and the parent is not yet registered);
4443 // put the parent in the machine registry then
4444 mediumLock.release();
4445 treeLock.release();
4446 alock.release();
4447 addMediumToRegistry(medium);
4448 alock.acquire();
4449 treeLock.acquire();
4450 mediumLock.acquire();
4451 medium->getFirstRegistryMachineId(uuidRegistryParent);
4452 }
4453 rc = diff->init(mParent,
4454 medium->getPreferredDiffFormat(),
4455 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4456 uuidRegistryParent);
4457 if (FAILED(rc)) return rc;
4458
4459 /* Apply the normal locking logic to the entire chain. */
4460 MediumLockList *pMediumLockList(new MediumLockList());
4461 mediumLock.release();
4462 treeLock.release();
4463 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4464 true /* fMediumLockWrite */,
4465 medium,
4466 *pMediumLockList);
4467 treeLock.acquire();
4468 mediumLock.acquire();
4469 if (SUCCEEDED(rc))
4470 {
4471 mediumLock.release();
4472 treeLock.release();
4473 rc = pMediumLockList->Lock();
4474 treeLock.acquire();
4475 mediumLock.acquire();
4476 if (FAILED(rc))
4477 setError(rc,
4478 tr("Could not lock medium when creating diff '%s'"),
4479 diff->getLocationFull().c_str());
4480 else
4481 {
4482 /* will release the lock before the potentially lengthy
4483 * operation, so protect with the special state */
4484 MachineState_T oldState = mData->mMachineState;
4485 setMachineState(MachineState_SettingUp);
4486
4487 mediumLock.release();
4488 treeLock.release();
4489 alock.release();
4490
4491 rc = medium->createDiffStorage(diff,
4492 MediumVariant_Standard,
4493 pMediumLockList,
4494 NULL /* aProgress */,
4495 true /* aWait */);
4496
4497 alock.acquire();
4498 treeLock.acquire();
4499 mediumLock.acquire();
4500
4501 setMachineState(oldState);
4502 }
4503 }
4504
4505 /* Unlock the media and free the associated memory. */
4506 delete pMediumLockList;
4507
4508 if (FAILED(rc)) return rc;
4509
4510 /* use the created diff for the actual attachment */
4511 medium = diff;
4512 mediumCaller.attach(medium);
4513 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4514 mediumLock.attach(medium);
4515 }
4516 while (0);
4517
4518 ComObjPtr<MediumAttachment> attachment;
4519 attachment.createObject();
4520 rc = attachment->init(this,
4521 medium,
4522 aControllerName,
4523 aControllerPort,
4524 aDevice,
4525 aType,
4526 fIndirect,
4527 false /* fPassthrough */,
4528 false /* fTempEject */,
4529 false /* fNonRotational */,
4530 false /* fDiscard */,
4531 Utf8Str::Empty);
4532 if (FAILED(rc)) return rc;
4533
4534 if (associate && !medium.isNull())
4535 {
4536 // as the last step, associate the medium to the VM
4537 rc = medium->addBackReference(mData->mUuid);
4538 // here we can fail because of Deleting, or being in process of creating a Diff
4539 if (FAILED(rc)) return rc;
4540
4541 mediumLock.release();
4542 treeLock.release();
4543 alock.release();
4544 addMediumToRegistry(medium);
4545 alock.acquire();
4546 treeLock.acquire();
4547 mediumLock.acquire();
4548 }
4549
4550 /* success: finally remember the attachment */
4551 setModified(IsModified_Storage);
4552 mMediaData.backup();
4553 mMediaData->mAttachments.push_back(attachment);
4554
4555 mediumLock.release();
4556 treeLock.release();
4557 alock.release();
4558
4559 if (fHotplug || fSilent)
4560 {
4561 if (!medium.isNull())
4562 {
4563 MediumLockList *pMediumLockList(new MediumLockList());
4564
4565 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4566 true /* fMediumLockWrite */,
4567 NULL,
4568 *pMediumLockList);
4569 alock.acquire();
4570 if (FAILED(rc))
4571 delete pMediumLockList;
4572 else
4573 {
4574 mData->mSession.mLockedMedia.Unlock();
4575 alock.release();
4576 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4577 mData->mSession.mLockedMedia.Lock();
4578 alock.acquire();
4579 }
4580 alock.release();
4581 }
4582
4583 if (SUCCEEDED(rc))
4584 {
4585 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4586 /* Remove lock list in case of error. */
4587 if (FAILED(rc))
4588 {
4589 mData->mSession.mLockedMedia.Unlock();
4590 mData->mSession.mLockedMedia.Remove(attachment);
4591 mData->mSession.mLockedMedia.Lock();
4592 }
4593 }
4594 }
4595
4596 mParent->saveModifiedRegistries();
4597
4598 return rc;
4599}
4600
4601STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4602 LONG aDevice)
4603{
4604 CheckComArgStrNotEmptyOrNull(aControllerName);
4605
4606 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4607 aControllerName, aControllerPort, aDevice));
4608
4609 AutoCaller autoCaller(this);
4610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4611
4612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4613
4614 HRESULT rc = checkStateDependency(MutableStateDep);
4615 if (FAILED(rc)) return rc;
4616
4617 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4618
4619 /* Check for an existing controller. */
4620 ComObjPtr<StorageController> ctl;
4621 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4622 if (FAILED(rc)) return rc;
4623
4624 StorageControllerType_T ctrlType;
4625 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4626 if (FAILED(rc))
4627 return setError(E_FAIL,
4628 tr("Could not get type of controller '%ls'"),
4629 aControllerName);
4630
4631 bool fSilent = false;
4632 Utf8Str strReconfig;
4633
4634 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4635 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4636 if (FAILED(rc))
4637 return rc;
4638 if ( mData->mMachineState == MachineState_Paused
4639 && strReconfig == "1")
4640 fSilent = true;
4641
4642 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4643 bool fHotplug = false;
4644 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4645 fHotplug = true;
4646
4647 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4648 return setError(VBOX_E_INVALID_VM_STATE,
4649 tr("Controller '%ls' does not support hotplugging"),
4650 aControllerName);
4651
4652 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4653 aControllerName,
4654 aControllerPort,
4655 aDevice);
4656 if (!pAttach)
4657 return setError(VBOX_E_OBJECT_NOT_FOUND,
4658 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4659 aDevice, aControllerPort, aControllerName);
4660
4661 /*
4662 * The VM has to detach the device before we delete any implicit diffs.
4663 * If this fails we can roll back without loosing data.
4664 */
4665 if (fHotplug || fSilent)
4666 {
4667 alock.release();
4668 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4669 alock.acquire();
4670 }
4671 if (FAILED(rc)) return rc;
4672
4673 /* If we are here everything went well and we can delete the implicit now. */
4674 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4675
4676 alock.release();
4677
4678 mParent->saveModifiedRegistries();
4679
4680 return rc;
4681}
4682
4683STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4684 LONG aDevice, BOOL aPassthrough)
4685{
4686 CheckComArgStrNotEmptyOrNull(aControllerName);
4687
4688 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4689 aControllerName, aControllerPort, aDevice, aPassthrough));
4690
4691 AutoCaller autoCaller(this);
4692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4693
4694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4695
4696 HRESULT rc = checkStateDependency(MutableStateDep);
4697 if (FAILED(rc)) return rc;
4698
4699 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4700
4701 if (Global::IsOnlineOrTransient(mData->mMachineState))
4702 return setError(VBOX_E_INVALID_VM_STATE,
4703 tr("Invalid machine state: %s"),
4704 Global::stringifyMachineState(mData->mMachineState));
4705
4706 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4707 aControllerName,
4708 aControllerPort,
4709 aDevice);
4710 if (!pAttach)
4711 return setError(VBOX_E_OBJECT_NOT_FOUND,
4712 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4713 aDevice, aControllerPort, aControllerName);
4714
4715
4716 setModified(IsModified_Storage);
4717 mMediaData.backup();
4718
4719 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4720
4721 if (pAttach->getType() != DeviceType_DVD)
4722 return setError(E_INVALIDARG,
4723 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4724 aDevice, aControllerPort, aControllerName);
4725 pAttach->updatePassthrough(!!aPassthrough);
4726
4727 return S_OK;
4728}
4729
4730STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4731 LONG aDevice, BOOL aTemporaryEject)
4732{
4733 CheckComArgStrNotEmptyOrNull(aControllerName);
4734
4735 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4736 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4737
4738 AutoCaller autoCaller(this);
4739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4740
4741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4742
4743 HRESULT rc = checkStateDependency(MutableStateDep);
4744 if (FAILED(rc)) return rc;
4745
4746 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4747 aControllerName,
4748 aControllerPort,
4749 aDevice);
4750 if (!pAttach)
4751 return setError(VBOX_E_OBJECT_NOT_FOUND,
4752 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4753 aDevice, aControllerPort, aControllerName);
4754
4755
4756 setModified(IsModified_Storage);
4757 mMediaData.backup();
4758
4759 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4760
4761 if (pAttach->getType() != DeviceType_DVD)
4762 return setError(E_INVALIDARG,
4763 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4764 aDevice, aControllerPort, aControllerName);
4765 pAttach->updateTempEject(!!aTemporaryEject);
4766
4767 return S_OK;
4768}
4769
4770STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4771 LONG aDevice, BOOL aNonRotational)
4772{
4773 CheckComArgStrNotEmptyOrNull(aControllerName);
4774
4775 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4776 aControllerName, aControllerPort, aDevice, aNonRotational));
4777
4778 AutoCaller autoCaller(this);
4779 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4780
4781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4782
4783 HRESULT rc = checkStateDependency(MutableStateDep);
4784 if (FAILED(rc)) return rc;
4785
4786 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4787
4788 if (Global::IsOnlineOrTransient(mData->mMachineState))
4789 return setError(VBOX_E_INVALID_VM_STATE,
4790 tr("Invalid machine state: %s"),
4791 Global::stringifyMachineState(mData->mMachineState));
4792
4793 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4794 aControllerName,
4795 aControllerPort,
4796 aDevice);
4797 if (!pAttach)
4798 return setError(VBOX_E_OBJECT_NOT_FOUND,
4799 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4800 aDevice, aControllerPort, aControllerName);
4801
4802
4803 setModified(IsModified_Storage);
4804 mMediaData.backup();
4805
4806 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4807
4808 if (pAttach->getType() != DeviceType_HardDisk)
4809 return setError(E_INVALIDARG,
4810 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4811 aDevice, aControllerPort, aControllerName);
4812 pAttach->updateNonRotational(!!aNonRotational);
4813
4814 return S_OK;
4815}
4816
4817STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4818 LONG aDevice, BOOL aDiscard)
4819{
4820 CheckComArgStrNotEmptyOrNull(aControllerName);
4821
4822 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4823 aControllerName, aControllerPort, aDevice, aDiscard));
4824
4825 AutoCaller autoCaller(this);
4826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4827
4828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4829
4830 HRESULT rc = checkStateDependency(MutableStateDep);
4831 if (FAILED(rc)) return rc;
4832
4833 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4834
4835 if (Global::IsOnlineOrTransient(mData->mMachineState))
4836 return setError(VBOX_E_INVALID_VM_STATE,
4837 tr("Invalid machine state: %s"),
4838 Global::stringifyMachineState(mData->mMachineState));
4839
4840 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4841 aControllerName,
4842 aControllerPort,
4843 aDevice);
4844 if (!pAttach)
4845 return setError(VBOX_E_OBJECT_NOT_FOUND,
4846 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4847 aDevice, aControllerPort, aControllerName);
4848
4849
4850 setModified(IsModified_Storage);
4851 mMediaData.backup();
4852
4853 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4854
4855 if (pAttach->getType() != DeviceType_HardDisk)
4856 return setError(E_INVALIDARG,
4857 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4858 aDevice, aControllerPort, aControllerName);
4859 pAttach->updateDiscard(!!aDiscard);
4860
4861 return S_OK;
4862}
4863
4864STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4865 LONG aDevice)
4866{
4867 int rc = S_OK;
4868 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4869 aControllerName, aControllerPort, aDevice));
4870
4871 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4872
4873 return rc;
4874}
4875
4876STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4877 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4878{
4879 CheckComArgStrNotEmptyOrNull(aControllerName);
4880
4881 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4882 aControllerName, aControllerPort, aDevice));
4883
4884 AutoCaller autoCaller(this);
4885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4886
4887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4888
4889 HRESULT rc = checkStateDependency(MutableStateDep);
4890 if (FAILED(rc)) return rc;
4891
4892 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4893
4894 if (Global::IsOnlineOrTransient(mData->mMachineState))
4895 return setError(VBOX_E_INVALID_VM_STATE,
4896 tr("Invalid machine state: %s"),
4897 Global::stringifyMachineState(mData->mMachineState));
4898
4899 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4900 aControllerName,
4901 aControllerPort,
4902 aDevice);
4903 if (!pAttach)
4904 return setError(VBOX_E_OBJECT_NOT_FOUND,
4905 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4906 aDevice, aControllerPort, aControllerName);
4907
4908
4909 setModified(IsModified_Storage);
4910 mMediaData.backup();
4911
4912 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4913 if (aBandwidthGroup && group.isNull())
4914 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4915
4916 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4917
4918 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4919 if (strBandwidthGroupOld.isNotEmpty())
4920 {
4921 /* Get the bandwidth group object and release it - this must not fail. */
4922 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4923 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4924 Assert(SUCCEEDED(rc));
4925
4926 pBandwidthGroupOld->release();
4927 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4928 }
4929
4930 if (!group.isNull())
4931 {
4932 group->reference();
4933 pAttach->updateBandwidthGroup(group->getName());
4934 }
4935
4936 return S_OK;
4937}
4938
4939STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4940 LONG aControllerPort,
4941 LONG aDevice,
4942 DeviceType_T aType)
4943{
4944 HRESULT rc = S_OK;
4945
4946 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4947 aControllerName, aControllerPort, aDevice, aType));
4948
4949 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4950
4951 return rc;
4952}
4953
4954
4955
4956STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4957 LONG aControllerPort,
4958 LONG aDevice,
4959 BOOL aForce)
4960{
4961 int rc = S_OK;
4962 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4963 aControllerName, aControllerPort, aForce));
4964
4965 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4966
4967 return rc;
4968}
4969
4970STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4971 LONG aControllerPort,
4972 LONG aDevice,
4973 IMedium *aMedium,
4974 BOOL aForce)
4975{
4976 int rc = S_OK;
4977 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4978 aControllerName, aControllerPort, aDevice, aForce));
4979
4980 CheckComArgStrNotEmptyOrNull(aControllerName);
4981
4982 AutoCaller autoCaller(this);
4983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4984
4985 // request the host lock first, since might be calling Host methods for getting host drives;
4986 // next, protect the media tree all the while we're in here, as well as our member variables
4987 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4988 this->lockHandle(),
4989 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4990
4991 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4992 aControllerName,
4993 aControllerPort,
4994 aDevice);
4995 if (pAttach.isNull())
4996 return setError(VBOX_E_OBJECT_NOT_FOUND,
4997 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4998 aDevice, aControllerPort, aControllerName);
4999
5000 /* Remember previously mounted medium. The medium before taking the
5001 * backup is not necessarily the same thing. */
5002 ComObjPtr<Medium> oldmedium;
5003 oldmedium = pAttach->getMedium();
5004
5005 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
5006 if (aMedium && pMedium.isNull())
5007 return setError(E_INVALIDARG, "The given medium pointer is invalid");
5008
5009 AutoCaller mediumCaller(pMedium);
5010 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
5011
5012 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
5013 if (pMedium)
5014 {
5015 DeviceType_T mediumType = pAttach->getType();
5016 switch (mediumType)
5017 {
5018 case DeviceType_DVD:
5019 case DeviceType_Floppy:
5020 break;
5021
5022 default:
5023 return setError(VBOX_E_INVALID_OBJECT_STATE,
5024 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
5025 aControllerPort,
5026 aDevice,
5027 aControllerName);
5028 }
5029 }
5030
5031 setModified(IsModified_Storage);
5032 mMediaData.backup();
5033
5034 {
5035 // The backup operation makes the pAttach reference point to the
5036 // old settings. Re-get the correct reference.
5037 pAttach = findAttachment(mMediaData->mAttachments,
5038 aControllerName,
5039 aControllerPort,
5040 aDevice);
5041 if (!oldmedium.isNull())
5042 oldmedium->removeBackReference(mData->mUuid);
5043 if (!pMedium.isNull())
5044 {
5045 pMedium->addBackReference(mData->mUuid);
5046
5047 mediumLock.release();
5048 multiLock.release();
5049 addMediumToRegistry(pMedium);
5050 multiLock.acquire();
5051 mediumLock.acquire();
5052 }
5053
5054 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5055 pAttach->updateMedium(pMedium);
5056 }
5057
5058 setModified(IsModified_Storage);
5059
5060 mediumLock.release();
5061 multiLock.release();
5062 rc = onMediumChange(pAttach, aForce);
5063 multiLock.acquire();
5064 mediumLock.acquire();
5065
5066 /* On error roll back this change only. */
5067 if (FAILED(rc))
5068 {
5069 if (!pMedium.isNull())
5070 pMedium->removeBackReference(mData->mUuid);
5071 pAttach = findAttachment(mMediaData->mAttachments,
5072 aControllerName,
5073 aControllerPort,
5074 aDevice);
5075 /* If the attachment is gone in the meantime, bail out. */
5076 if (pAttach.isNull())
5077 return rc;
5078 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
5079 if (!oldmedium.isNull())
5080 oldmedium->addBackReference(mData->mUuid);
5081 pAttach->updateMedium(oldmedium);
5082 }
5083
5084 mediumLock.release();
5085 multiLock.release();
5086
5087 mParent->saveModifiedRegistries();
5088
5089 return rc;
5090}
5091
5092STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
5093 LONG aControllerPort,
5094 LONG aDevice,
5095 IMedium **aMedium)
5096{
5097 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5098 aControllerName, aControllerPort, aDevice));
5099
5100 CheckComArgStrNotEmptyOrNull(aControllerName);
5101 CheckComArgOutPointerValid(aMedium);
5102
5103 AutoCaller autoCaller(this);
5104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5105
5106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5107
5108 *aMedium = NULL;
5109
5110 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5111 aControllerName,
5112 aControllerPort,
5113 aDevice);
5114 if (pAttach.isNull())
5115 return setError(VBOX_E_OBJECT_NOT_FOUND,
5116 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5117 aDevice, aControllerPort, aControllerName);
5118
5119 pAttach->getMedium().queryInterfaceTo(aMedium);
5120
5121 return S_OK;
5122}
5123
5124STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
5125{
5126 CheckComArgOutPointerValid(port);
5127 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
5128
5129 AutoCaller autoCaller(this);
5130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5131
5132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5133
5134 mSerialPorts[slot].queryInterfaceTo(port);
5135
5136 return S_OK;
5137}
5138
5139STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
5140{
5141 CheckComArgOutPointerValid(port);
5142 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
5143
5144 AutoCaller autoCaller(this);
5145 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5146
5147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5148
5149 mParallelPorts[slot].queryInterfaceTo(port);
5150
5151 return S_OK;
5152}
5153
5154STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
5155{
5156 CheckComArgOutPointerValid(adapter);
5157 /* Do not assert if slot is out of range, just return the advertised
5158 status. testdriver/vbox.py triggers this in logVmInfo. */
5159 if (slot >= mNetworkAdapters.size())
5160 return setError(E_INVALIDARG,
5161 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5162 slot, mNetworkAdapters.size());
5163
5164 AutoCaller autoCaller(this);
5165 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5166
5167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5168
5169 mNetworkAdapters[slot].queryInterfaceTo(adapter);
5170
5171 return S_OK;
5172}
5173
5174STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
5175{
5176 CheckComArgOutSafeArrayPointerValid(aKeys);
5177
5178 AutoCaller autoCaller(this);
5179 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5180
5181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5182
5183 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
5184 int i = 0;
5185 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5186 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5187 ++it, ++i)
5188 {
5189 const Utf8Str &strKey = it->first;
5190 strKey.cloneTo(&saKeys[i]);
5191 }
5192 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
5193
5194 return S_OK;
5195 }
5196
5197 /**
5198 * @note Locks this object for reading.
5199 */
5200STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
5201 BSTR *aValue)
5202{
5203 CheckComArgStrNotEmptyOrNull(aKey);
5204 CheckComArgOutPointerValid(aValue);
5205
5206 AutoCaller autoCaller(this);
5207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5208
5209 /* start with nothing found */
5210 Bstr bstrResult("");
5211
5212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5213
5214 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
5215 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5216 // found:
5217 bstrResult = it->second; // source is a Utf8Str
5218
5219 /* return the result to caller (may be empty) */
5220 bstrResult.cloneTo(aValue);
5221
5222 return S_OK;
5223}
5224
5225 /**
5226 * @note Locks mParent for writing + this object for writing.
5227 */
5228STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
5229{
5230 CheckComArgStrNotEmptyOrNull(aKey);
5231
5232 AutoCaller autoCaller(this);
5233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5234
5235 Utf8Str strKey(aKey);
5236 Utf8Str strValue(aValue);
5237 Utf8Str strOldValue; // empty
5238
5239 // locking note: we only hold the read lock briefly to look up the old value,
5240 // then release it and call the onExtraCanChange callbacks. There is a small
5241 // chance of a race insofar as the callback might be called twice if two callers
5242 // change the same key at the same time, but that's a much better solution
5243 // than the deadlock we had here before. The actual changing of the extradata
5244 // is then performed under the write lock and race-free.
5245
5246 // look up the old value first; if nothing has changed then we need not do anything
5247 {
5248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5249 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
5250 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5251 strOldValue = it->second;
5252 }
5253
5254 bool fChanged;
5255 if ((fChanged = (strOldValue != strValue)))
5256 {
5257 // ask for permission from all listeners outside the locks;
5258 // onExtraDataCanChange() only briefly requests the VirtualBox
5259 // lock to copy the list of callbacks to invoke
5260 Bstr error;
5261 Bstr bstrValue(aValue);
5262
5263 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
5264 {
5265 const char *sep = error.isEmpty() ? "" : ": ";
5266 CBSTR err = error.raw();
5267 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
5268 sep, err));
5269 return setError(E_ACCESSDENIED,
5270 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
5271 aKey,
5272 bstrValue.raw(),
5273 sep,
5274 err);
5275 }
5276
5277 // data is changing and change not vetoed: then write it out under the lock
5278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5279
5280 if (isSnapshotMachine())
5281 {
5282 HRESULT rc = checkStateDependency(MutableStateDep);
5283 if (FAILED(rc)) return rc;
5284 }
5285
5286 if (strValue.isEmpty())
5287 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5288 else
5289 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5290 // creates a new key if needed
5291
5292 bool fNeedsGlobalSaveSettings = false;
5293 saveSettings(&fNeedsGlobalSaveSettings);
5294
5295 if (fNeedsGlobalSaveSettings)
5296 {
5297 // save the global settings; for that we should hold only the VirtualBox lock
5298 alock.release();
5299 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5300 mParent->saveSettings();
5301 }
5302 }
5303
5304 // fire notification outside the lock
5305 if (fChanged)
5306 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5307
5308 return S_OK;
5309}
5310
5311STDMETHODIMP Machine::SaveSettings()
5312{
5313 AutoCaller autoCaller(this);
5314 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5315
5316 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5317
5318 /* when there was auto-conversion, we want to save the file even if
5319 * the VM is saved */
5320 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5321 if (FAILED(rc)) return rc;
5322
5323 /* the settings file path may never be null */
5324 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5325
5326 /* save all VM data excluding snapshots */
5327 bool fNeedsGlobalSaveSettings = false;
5328 rc = saveSettings(&fNeedsGlobalSaveSettings);
5329 mlock.release();
5330
5331 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5332 {
5333 // save the global settings; for that we should hold only the VirtualBox lock
5334 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5335 rc = mParent->saveSettings();
5336 }
5337
5338 return rc;
5339}
5340
5341STDMETHODIMP Machine::DiscardSettings()
5342{
5343 AutoCaller autoCaller(this);
5344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5345
5346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5347
5348 HRESULT rc = checkStateDependency(MutableStateDep);
5349 if (FAILED(rc)) return rc;
5350
5351 /*
5352 * during this rollback, the session will be notified if data has
5353 * been actually changed
5354 */
5355 rollback(true /* aNotify */);
5356
5357 return S_OK;
5358}
5359
5360/** @note Locks objects! */
5361STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5362 ComSafeArrayOut(IMedium*, aMedia))
5363{
5364 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5365 AutoLimitedCaller autoCaller(this);
5366 AssertComRCReturnRC(autoCaller.rc());
5367
5368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5369
5370 Guid id(getId());
5371
5372 if (mData->mSession.mState != SessionState_Unlocked)
5373 return setError(VBOX_E_INVALID_OBJECT_STATE,
5374 tr("Cannot unregister the machine '%s' while it is locked"),
5375 mUserData->s.strName.c_str());
5376
5377 // wait for state dependents to drop to zero
5378 ensureNoStateDependencies();
5379
5380 if (!mData->mAccessible)
5381 {
5382 // inaccessible maschines can only be unregistered; uninitialize ourselves
5383 // here because currently there may be no unregistered that are inaccessible
5384 // (this state combination is not supported). Note releasing the caller and
5385 // leaving the lock before calling uninit()
5386 alock.release();
5387 autoCaller.release();
5388
5389 uninit();
5390
5391 mParent->unregisterMachine(this, id);
5392 // calls VirtualBox::saveSettings()
5393
5394 return S_OK;
5395 }
5396
5397 HRESULT rc = S_OK;
5398
5399 // discard saved state
5400 if (mData->mMachineState == MachineState_Saved)
5401 {
5402 // add the saved state file to the list of files the caller should delete
5403 Assert(!mSSData->strStateFilePath.isEmpty());
5404 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5405
5406 mSSData->strStateFilePath.setNull();
5407
5408 // unconditionally set the machine state to powered off, we now
5409 // know no session has locked the machine
5410 mData->mMachineState = MachineState_PoweredOff;
5411 }
5412
5413 size_t cSnapshots = 0;
5414 if (mData->mFirstSnapshot)
5415 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5416 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5417 // fail now before we start detaching media
5418 return setError(VBOX_E_INVALID_OBJECT_STATE,
5419 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5420 mUserData->s.strName.c_str(), cSnapshots);
5421
5422 // This list collects the medium objects from all medium attachments
5423 // which we will detach from the machine and its snapshots, in a specific
5424 // order which allows for closing all media without getting "media in use"
5425 // errors, simply by going through the list from the front to the back:
5426 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5427 // and must be closed before the parent media from the snapshots, or closing the parents
5428 // will fail because they still have children);
5429 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5430 // the root ("first") snapshot of the machine.
5431 MediaList llMedia;
5432
5433 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5434 && mMediaData->mAttachments.size()
5435 )
5436 {
5437 // we have media attachments: detach them all and add the Medium objects to our list
5438 if (cleanupMode != CleanupMode_UnregisterOnly)
5439 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5440 else
5441 return setError(VBOX_E_INVALID_OBJECT_STATE,
5442 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5443 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5444 }
5445
5446 if (cSnapshots)
5447 {
5448 // autoCleanup must be true here, or we would have failed above
5449
5450 // add the media from the medium attachments of the snapshots to llMedia
5451 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5452 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5453 // into the children first
5454
5455 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5456 MachineState_T oldState = mData->mMachineState;
5457 mData->mMachineState = MachineState_DeletingSnapshot;
5458
5459 // make a copy of the first snapshot so the refcount does not drop to 0
5460 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5461 // because of the AutoCaller voodoo)
5462 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5463
5464 // GO!
5465 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5466
5467 mData->mMachineState = oldState;
5468 }
5469
5470 if (FAILED(rc))
5471 {
5472 rollbackMedia();
5473 return rc;
5474 }
5475
5476 // commit all the media changes made above
5477 commitMedia();
5478
5479 mData->mRegistered = false;
5480
5481 // machine lock no longer needed
5482 alock.release();
5483
5484 // return media to caller
5485 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5486 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5487
5488 mParent->unregisterMachine(this, id);
5489 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5490
5491 return S_OK;
5492}
5493
5494struct Machine::DeleteTask
5495{
5496 ComObjPtr<Machine> pMachine;
5497 RTCList<ComPtr<IMedium> > llMediums;
5498 StringsList llFilesToDelete;
5499 ComObjPtr<Progress> pProgress;
5500};
5501
5502STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5503{
5504 LogFlowFuncEnter();
5505
5506 AutoCaller autoCaller(this);
5507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5508
5509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5510
5511 HRESULT rc = checkStateDependency(MutableStateDep);
5512 if (FAILED(rc)) return rc;
5513
5514 if (mData->mRegistered)
5515 return setError(VBOX_E_INVALID_VM_STATE,
5516 tr("Cannot delete settings of a registered machine"));
5517
5518 DeleteTask *pTask = new DeleteTask;
5519 pTask->pMachine = this;
5520 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5521
5522 // collect files to delete
5523 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5524
5525 for (size_t i = 0; i < sfaMedia.size(); ++i)
5526 {
5527 IMedium *pIMedium(sfaMedia[i]);
5528 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5529 if (pMedium.isNull())
5530 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5531 SafeArray<BSTR> ids;
5532 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5533 if (FAILED(rc)) return rc;
5534 /* At this point the medium should not have any back references
5535 * anymore. If it has it is attached to another VM and *must* not
5536 * deleted. */
5537 if (ids.size() < 1)
5538 pTask->llMediums.append(pMedium);
5539 }
5540 if (mData->pMachineConfigFile->fileExists())
5541 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5542
5543 pTask->pProgress.createObject();
5544 pTask->pProgress->init(getVirtualBox(),
5545 static_cast<IMachine*>(this) /* aInitiator */,
5546 Bstr(tr("Deleting files")).raw(),
5547 true /* fCancellable */,
5548 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5549 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5550
5551 int vrc = RTThreadCreate(NULL,
5552 Machine::deleteThread,
5553 (void*)pTask,
5554 0,
5555 RTTHREADTYPE_MAIN_WORKER,
5556 0,
5557 "MachineDelete");
5558
5559 pTask->pProgress.queryInterfaceTo(aProgress);
5560
5561 if (RT_FAILURE(vrc))
5562 {
5563 delete pTask;
5564 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5565 }
5566
5567 LogFlowFuncLeave();
5568
5569 return S_OK;
5570}
5571
5572/**
5573 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5574 * calls Machine::deleteTaskWorker() on the actual machine object.
5575 * @param Thread
5576 * @param pvUser
5577 * @return
5578 */
5579/*static*/
5580DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5581{
5582 LogFlowFuncEnter();
5583
5584 DeleteTask *pTask = (DeleteTask*)pvUser;
5585 Assert(pTask);
5586 Assert(pTask->pMachine);
5587 Assert(pTask->pProgress);
5588
5589 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5590 pTask->pProgress->notifyComplete(rc);
5591
5592 delete pTask;
5593
5594 LogFlowFuncLeave();
5595
5596 NOREF(Thread);
5597
5598 return VINF_SUCCESS;
5599}
5600
5601/**
5602 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5603 * @param task
5604 * @return
5605 */
5606HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5607{
5608 AutoCaller autoCaller(this);
5609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5610
5611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5612
5613 HRESULT rc = S_OK;
5614
5615 try
5616 {
5617 ULONG uLogHistoryCount = 3;
5618 ComPtr<ISystemProperties> systemProperties;
5619 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5620 if (FAILED(rc)) throw rc;
5621
5622 if (!systemProperties.isNull())
5623 {
5624 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5625 if (FAILED(rc)) throw rc;
5626 }
5627
5628 MachineState_T oldState = mData->mMachineState;
5629 setMachineState(MachineState_SettingUp);
5630 alock.release();
5631 for (size_t i = 0; i < task.llMediums.size(); ++i)
5632 {
5633 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5634 {
5635 AutoCaller mac(pMedium);
5636 if (FAILED(mac.rc())) throw mac.rc();
5637 Utf8Str strLocation = pMedium->getLocationFull();
5638 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5639 if (FAILED(rc)) throw rc;
5640 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5641 }
5642 ComPtr<IProgress> pProgress2;
5643 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5644 if (FAILED(rc)) throw rc;
5645 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5646 if (FAILED(rc)) throw rc;
5647 /* Check the result of the asynchrony process. */
5648 LONG iRc;
5649 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5650 if (FAILED(rc)) throw rc;
5651 /* If the thread of the progress object has an error, then
5652 * retrieve the error info from there, or it'll be lost. */
5653 if (FAILED(iRc))
5654 throw setError(ProgressErrorInfo(pProgress2));
5655 }
5656 setMachineState(oldState);
5657 alock.acquire();
5658
5659 // delete the files pushed on the task list by Machine::Delete()
5660 // (this includes saved states of the machine and snapshots and
5661 // medium storage files from the IMedium list passed in, and the
5662 // machine XML file)
5663 StringsList::const_iterator it = task.llFilesToDelete.begin();
5664 while (it != task.llFilesToDelete.end())
5665 {
5666 const Utf8Str &strFile = *it;
5667 LogFunc(("Deleting file %s\n", strFile.c_str()));
5668 int vrc = RTFileDelete(strFile.c_str());
5669 if (RT_FAILURE(vrc))
5670 throw setError(VBOX_E_IPRT_ERROR,
5671 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5672
5673 ++it;
5674 if (it == task.llFilesToDelete.end())
5675 {
5676 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5677 if (FAILED(rc)) throw rc;
5678 break;
5679 }
5680
5681 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5682 if (FAILED(rc)) throw rc;
5683 }
5684
5685 /* delete the settings only when the file actually exists */
5686 if (mData->pMachineConfigFile->fileExists())
5687 {
5688 /* Delete any backup or uncommitted XML files. Ignore failures.
5689 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5690 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5691 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5692 RTFileDelete(otherXml.c_str());
5693 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5694 RTFileDelete(otherXml.c_str());
5695
5696 /* delete the Logs folder, nothing important should be left
5697 * there (we don't check for errors because the user might have
5698 * some private files there that we don't want to delete) */
5699 Utf8Str logFolder;
5700 getLogFolder(logFolder);
5701 Assert(logFolder.length());
5702 if (RTDirExists(logFolder.c_str()))
5703 {
5704 /* Delete all VBox.log[.N] files from the Logs folder
5705 * (this must be in sync with the rotation logic in
5706 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5707 * files that may have been created by the GUI. */
5708 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5709 logFolder.c_str(), RTPATH_DELIMITER);
5710 RTFileDelete(log.c_str());
5711 log = Utf8StrFmt("%s%cVBox.png",
5712 logFolder.c_str(), RTPATH_DELIMITER);
5713 RTFileDelete(log.c_str());
5714 for (int i = uLogHistoryCount; i > 0; i--)
5715 {
5716 log = Utf8StrFmt("%s%cVBox.log.%d",
5717 logFolder.c_str(), RTPATH_DELIMITER, i);
5718 RTFileDelete(log.c_str());
5719 log = Utf8StrFmt("%s%cVBox.png.%d",
5720 logFolder.c_str(), RTPATH_DELIMITER, i);
5721 RTFileDelete(log.c_str());
5722 }
5723
5724 RTDirRemove(logFolder.c_str());
5725 }
5726
5727 /* delete the Snapshots folder, nothing important should be left
5728 * there (we don't check for errors because the user might have
5729 * some private files there that we don't want to delete) */
5730 Utf8Str strFullSnapshotFolder;
5731 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5732 Assert(!strFullSnapshotFolder.isEmpty());
5733 if (RTDirExists(strFullSnapshotFolder.c_str()))
5734 RTDirRemove(strFullSnapshotFolder.c_str());
5735
5736 // delete the directory that contains the settings file, but only
5737 // if it matches the VM name
5738 Utf8Str settingsDir;
5739 if (isInOwnDir(&settingsDir))
5740 RTDirRemove(settingsDir.c_str());
5741 }
5742
5743 alock.release();
5744
5745 mParent->saveModifiedRegistries();
5746 }
5747 catch (HRESULT aRC) { rc = aRC; }
5748
5749 return rc;
5750}
5751
5752STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5753{
5754 CheckComArgOutPointerValid(aSnapshot);
5755
5756 AutoCaller autoCaller(this);
5757 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5758
5759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5760
5761 ComObjPtr<Snapshot> pSnapshot;
5762 HRESULT rc;
5763
5764 if (!aNameOrId || !*aNameOrId)
5765 // null case (caller wants root snapshot): findSnapshotById() handles this
5766 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5767 else
5768 {
5769 Guid uuid(aNameOrId);
5770 if (uuid.isValid())
5771 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5772 else
5773 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5774 }
5775 pSnapshot.queryInterfaceTo(aSnapshot);
5776
5777 return rc;
5778}
5779
5780STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5781{
5782 CheckComArgStrNotEmptyOrNull(aName);
5783 CheckComArgStrNotEmptyOrNull(aHostPath);
5784
5785 AutoCaller autoCaller(this);
5786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5787
5788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5789
5790 HRESULT rc = checkStateDependency(MutableStateDep);
5791 if (FAILED(rc)) return rc;
5792
5793 Utf8Str strName(aName);
5794
5795 ComObjPtr<SharedFolder> sharedFolder;
5796 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5797 if (SUCCEEDED(rc))
5798 return setError(VBOX_E_OBJECT_IN_USE,
5799 tr("Shared folder named '%s' already exists"),
5800 strName.c_str());
5801
5802 sharedFolder.createObject();
5803 rc = sharedFolder->init(getMachine(),
5804 strName,
5805 aHostPath,
5806 !!aWritable,
5807 !!aAutoMount,
5808 true /* fFailOnError */);
5809 if (FAILED(rc)) return rc;
5810
5811 setModified(IsModified_SharedFolders);
5812 mHWData.backup();
5813 mHWData->mSharedFolders.push_back(sharedFolder);
5814
5815 /* inform the direct session if any */
5816 alock.release();
5817 onSharedFolderChange();
5818
5819 return S_OK;
5820}
5821
5822STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5823{
5824 CheckComArgStrNotEmptyOrNull(aName);
5825
5826 AutoCaller autoCaller(this);
5827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5828
5829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5830
5831 HRESULT rc = checkStateDependency(MutableStateDep);
5832 if (FAILED(rc)) return rc;
5833
5834 ComObjPtr<SharedFolder> sharedFolder;
5835 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5836 if (FAILED(rc)) return rc;
5837
5838 setModified(IsModified_SharedFolders);
5839 mHWData.backup();
5840 mHWData->mSharedFolders.remove(sharedFolder);
5841
5842 /* inform the direct session if any */
5843 alock.release();
5844 onSharedFolderChange();
5845
5846 return S_OK;
5847}
5848
5849STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5850{
5851 CheckComArgOutPointerValid(aCanShow);
5852
5853 /* start with No */
5854 *aCanShow = FALSE;
5855
5856 AutoCaller autoCaller(this);
5857 AssertComRCReturnRC(autoCaller.rc());
5858
5859 ComPtr<IInternalSessionControl> directControl;
5860 {
5861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5862
5863 if (mData->mSession.mState != SessionState_Locked)
5864 return setError(VBOX_E_INVALID_VM_STATE,
5865 tr("Machine is not locked for session (session state: %s)"),
5866 Global::stringifySessionState(mData->mSession.mState));
5867
5868 directControl = mData->mSession.mDirectControl;
5869 }
5870
5871 /* ignore calls made after #OnSessionEnd() is called */
5872 if (!directControl)
5873 return S_OK;
5874
5875 LONG64 dummy;
5876 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5877}
5878
5879STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5880{
5881 CheckComArgOutPointerValid(aWinId);
5882
5883 AutoCaller autoCaller(this);
5884 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5885
5886 ComPtr<IInternalSessionControl> directControl;
5887 {
5888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5889
5890 if (mData->mSession.mState != SessionState_Locked)
5891 return setError(E_FAIL,
5892 tr("Machine is not locked for session (session state: %s)"),
5893 Global::stringifySessionState(mData->mSession.mState));
5894
5895 directControl = mData->mSession.mDirectControl;
5896 }
5897
5898 /* ignore calls made after #OnSessionEnd() is called */
5899 if (!directControl)
5900 return S_OK;
5901
5902 BOOL dummy;
5903 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5904}
5905
5906#ifdef VBOX_WITH_GUEST_PROPS
5907/**
5908 * Look up a guest property in VBoxSVC's internal structures.
5909 */
5910HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5911 BSTR *aValue,
5912 LONG64 *aTimestamp,
5913 BSTR *aFlags) const
5914{
5915 using namespace guestProp;
5916
5917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5918 Utf8Str strName(aName);
5919 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName);
5920
5921 if (it != mHWData->mGuestProperties.end())
5922 {
5923 char szFlags[MAX_FLAGS_LEN + 1];
5924 it->second.strValue.cloneTo(aValue);
5925 *aTimestamp = it->second.mTimestamp;
5926 writeFlags(it->second.mFlags, szFlags);
5927 Bstr(szFlags).cloneTo(aFlags);
5928 }
5929
5930 return S_OK;
5931}
5932
5933/**
5934 * Query the VM that a guest property belongs to for the property.
5935 * @returns E_ACCESSDENIED if the VM process is not available or not
5936 * currently handling queries and the lookup should then be done in
5937 * VBoxSVC.
5938 */
5939HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5940 BSTR *aValue,
5941 LONG64 *aTimestamp,
5942 BSTR *aFlags) const
5943{
5944 HRESULT rc;
5945 ComPtr<IInternalSessionControl> directControl;
5946 directControl = mData->mSession.mDirectControl;
5947
5948 /* fail if we were called after #OnSessionEnd() is called. This is a
5949 * silly race condition. */
5950
5951 if (!directControl)
5952 rc = E_ACCESSDENIED;
5953 else
5954 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5955 false /* isSetter */,
5956 aValue, aTimestamp, aFlags);
5957 return rc;
5958}
5959#endif // VBOX_WITH_GUEST_PROPS
5960
5961STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5962 BSTR *aValue,
5963 LONG64 *aTimestamp,
5964 BSTR *aFlags)
5965{
5966#ifndef VBOX_WITH_GUEST_PROPS
5967 ReturnComNotImplemented();
5968#else // VBOX_WITH_GUEST_PROPS
5969 CheckComArgStrNotEmptyOrNull(aName);
5970 CheckComArgOutPointerValid(aValue);
5971 CheckComArgOutPointerValid(aTimestamp);
5972 CheckComArgOutPointerValid(aFlags);
5973
5974 AutoCaller autoCaller(this);
5975 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5976
5977 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5978 if (rc == E_ACCESSDENIED)
5979 /* The VM is not running or the service is not (yet) accessible */
5980 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5981 return rc;
5982#endif // VBOX_WITH_GUEST_PROPS
5983}
5984
5985STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5986{
5987 LONG64 dummyTimestamp;
5988 Bstr dummyFlags;
5989 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5990}
5991
5992STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5993{
5994 Bstr dummyValue;
5995 Bstr dummyFlags;
5996 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5997}
5998
5999#ifdef VBOX_WITH_GUEST_PROPS
6000/**
6001 * Set a guest property in VBoxSVC's internal structures.
6002 */
6003HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
6004 IN_BSTR aFlags)
6005{
6006 using namespace guestProp;
6007
6008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6009 HRESULT rc = S_OK;
6010
6011 rc = checkStateDependency(MutableStateDep);
6012 if (FAILED(rc)) return rc;
6013
6014 try
6015 {
6016 Utf8Str utf8Name(aName);
6017 Utf8Str utf8Flags(aFlags);
6018 uint32_t fFlags = NILFLAG;
6019 if ( aFlags != NULL
6020 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)))
6021 return setError(E_INVALIDARG,
6022 tr("Invalid guest property flag values: '%ls'"),
6023 aFlags);
6024
6025 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
6026 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
6027 if (it == mHWData->mGuestProperties.end())
6028 {
6029 if (!fDelete)
6030 {
6031 setModified(IsModified_MachineData);
6032 mHWData.backupEx();
6033
6034 RTTIMESPEC time;
6035 HWData::GuestProperty prop;
6036 prop.strValue = aValue;
6037 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6038 prop.mFlags = fFlags;
6039 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
6040 }
6041 }
6042 else
6043 {
6044 if (it->second.mFlags & (RDONLYHOST))
6045 {
6046 rc = setError(E_ACCESSDENIED,
6047 tr("The property '%ls' cannot be changed by the host"),
6048 aName);
6049 }
6050 else
6051 {
6052 setModified(IsModified_MachineData);
6053 mHWData.backupEx();
6054
6055 /* The backupEx() operation invalidates our iterator,
6056 * so get a new one. */
6057 it = mHWData->mGuestProperties.find(utf8Name);
6058 Assert(it != mHWData->mGuestProperties.end());
6059
6060 if (!fDelete)
6061 {
6062 RTTIMESPEC time;
6063 it->second.strValue = aValue;
6064 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
6065 it->second.mFlags = fFlags;
6066 }
6067 else
6068 mHWData->mGuestProperties.erase(it);
6069 }
6070 }
6071
6072 if ( SUCCEEDED(rc)
6073 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
6074 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
6075 RTSTR_MAX,
6076 utf8Name.c_str(),
6077 RTSTR_MAX,
6078 NULL)
6079 )
6080 )
6081 {
6082 alock.release();
6083
6084 mParent->onGuestPropertyChange(mData->mUuid, aName,
6085 aValue ? aValue : Bstr("").raw(),
6086 aFlags ? aFlags : Bstr("").raw());
6087 }
6088 }
6089 catch (std::bad_alloc &)
6090 {
6091 rc = E_OUTOFMEMORY;
6092 }
6093
6094 return rc;
6095}
6096
6097/**
6098 * Set a property on the VM that that property belongs to.
6099 * @returns E_ACCESSDENIED if the VM process is not available or not
6100 * currently handling queries and the setting should then be done in
6101 * VBoxSVC.
6102 */
6103HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
6104 IN_BSTR aFlags)
6105{
6106 HRESULT rc;
6107
6108 try
6109 {
6110 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
6111
6112 BSTR dummy = NULL; /* will not be changed (setter) */
6113 LONG64 dummy64;
6114 if (!directControl)
6115 rc = E_ACCESSDENIED;
6116 else
6117 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
6118 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
6119 true /* isSetter */,
6120 &dummy, &dummy64, &dummy);
6121 }
6122 catch (std::bad_alloc &)
6123 {
6124 rc = E_OUTOFMEMORY;
6125 }
6126
6127 return rc;
6128}
6129#endif // VBOX_WITH_GUEST_PROPS
6130
6131STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
6132 IN_BSTR aFlags)
6133{
6134#ifndef VBOX_WITH_GUEST_PROPS
6135 ReturnComNotImplemented();
6136#else // VBOX_WITH_GUEST_PROPS
6137 CheckComArgStrNotEmptyOrNull(aName);
6138 CheckComArgMaybeNull(aFlags);
6139 CheckComArgMaybeNull(aValue);
6140
6141 AutoCaller autoCaller(this);
6142 if (FAILED(autoCaller.rc()))
6143 return autoCaller.rc();
6144
6145 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
6146 if (rc == E_ACCESSDENIED)
6147 /* The VM is not running or the service is not (yet) accessible */
6148 rc = setGuestPropertyToService(aName, aValue, aFlags);
6149 return rc;
6150#endif // VBOX_WITH_GUEST_PROPS
6151}
6152
6153STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
6154{
6155 return SetGuestProperty(aName, aValue, NULL);
6156}
6157
6158STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
6159{
6160 return SetGuestProperty(aName, NULL, NULL);
6161}
6162
6163#ifdef VBOX_WITH_GUEST_PROPS
6164/**
6165 * Enumerate the guest properties in VBoxSVC's internal structures.
6166 */
6167HRESULT Machine::enumerateGuestPropertiesInService
6168 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6169 ComSafeArrayOut(BSTR, aValues),
6170 ComSafeArrayOut(LONG64, aTimestamps),
6171 ComSafeArrayOut(BSTR, aFlags))
6172{
6173 using namespace guestProp;
6174
6175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6176 Utf8Str strPatterns(aPatterns);
6177
6178 HWData::GuestPropertyMap propMap;
6179
6180 /*
6181 * Look for matching patterns and build up a list.
6182 */
6183 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
6184 while (it != mHWData->mGuestProperties.end())
6185 {
6186 if ( strPatterns.isEmpty()
6187 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6188 RTSTR_MAX,
6189 it->first.c_str(),
6190 RTSTR_MAX,
6191 NULL)
6192 )
6193 {
6194 propMap.insert(*it);
6195 }
6196
6197 it++;
6198 }
6199
6200 alock.release();
6201
6202 /*
6203 * And build up the arrays for returning the property information.
6204 */
6205 size_t cEntries = propMap.size();
6206 SafeArray<BSTR> names(cEntries);
6207 SafeArray<BSTR> values(cEntries);
6208 SafeArray<LONG64> timestamps(cEntries);
6209 SafeArray<BSTR> flags(cEntries);
6210 size_t iProp = 0;
6211
6212 it = propMap.begin();
6213 while (it != propMap.end())
6214 {
6215 char szFlags[MAX_FLAGS_LEN + 1];
6216 it->first.cloneTo(&names[iProp]);
6217 it->second.strValue.cloneTo(&values[iProp]);
6218 timestamps[iProp] = it->second.mTimestamp;
6219 writeFlags(it->second.mFlags, szFlags);
6220 Bstr(szFlags).cloneTo(&flags[iProp++]);
6221 it++;
6222 }
6223 names.detachTo(ComSafeArrayOutArg(aNames));
6224 values.detachTo(ComSafeArrayOutArg(aValues));
6225 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
6226 flags.detachTo(ComSafeArrayOutArg(aFlags));
6227 return S_OK;
6228}
6229
6230/**
6231 * Enumerate the properties managed by a VM.
6232 * @returns E_ACCESSDENIED if the VM process is not available or not
6233 * currently handling queries and the setting should then be done in
6234 * VBoxSVC.
6235 */
6236HRESULT Machine::enumerateGuestPropertiesOnVM
6237 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
6238 ComSafeArrayOut(BSTR, aValues),
6239 ComSafeArrayOut(LONG64, aTimestamps),
6240 ComSafeArrayOut(BSTR, aFlags))
6241{
6242 HRESULT rc;
6243 ComPtr<IInternalSessionControl> directControl;
6244 directControl = mData->mSession.mDirectControl;
6245
6246 if (!directControl)
6247 rc = E_ACCESSDENIED;
6248 else
6249 rc = directControl->EnumerateGuestProperties
6250 (aPatterns, ComSafeArrayOutArg(aNames),
6251 ComSafeArrayOutArg(aValues),
6252 ComSafeArrayOutArg(aTimestamps),
6253 ComSafeArrayOutArg(aFlags));
6254 return rc;
6255}
6256#endif // VBOX_WITH_GUEST_PROPS
6257
6258STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
6259 ComSafeArrayOut(BSTR, aNames),
6260 ComSafeArrayOut(BSTR, aValues),
6261 ComSafeArrayOut(LONG64, aTimestamps),
6262 ComSafeArrayOut(BSTR, aFlags))
6263{
6264#ifndef VBOX_WITH_GUEST_PROPS
6265 ReturnComNotImplemented();
6266#else // VBOX_WITH_GUEST_PROPS
6267 CheckComArgMaybeNull(aPatterns);
6268 CheckComArgOutSafeArrayPointerValid(aNames);
6269 CheckComArgOutSafeArrayPointerValid(aValues);
6270 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6271 CheckComArgOutSafeArrayPointerValid(aFlags);
6272
6273 AutoCaller autoCaller(this);
6274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6275
6276 HRESULT rc = enumerateGuestPropertiesOnVM
6277 (aPatterns, ComSafeArrayOutArg(aNames),
6278 ComSafeArrayOutArg(aValues),
6279 ComSafeArrayOutArg(aTimestamps),
6280 ComSafeArrayOutArg(aFlags));
6281 if (rc == E_ACCESSDENIED)
6282 /* The VM is not running or the service is not (yet) accessible */
6283 rc = enumerateGuestPropertiesInService
6284 (aPatterns, ComSafeArrayOutArg(aNames),
6285 ComSafeArrayOutArg(aValues),
6286 ComSafeArrayOutArg(aTimestamps),
6287 ComSafeArrayOutArg(aFlags));
6288 return rc;
6289#endif // VBOX_WITH_GUEST_PROPS
6290}
6291
6292STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6293 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6294{
6295 MediaData::AttachmentList atts;
6296
6297 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6298 if (FAILED(rc)) return rc;
6299
6300 SafeIfaceArray<IMediumAttachment> attachments(atts);
6301 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6302
6303 return S_OK;
6304}
6305
6306STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6307 LONG aControllerPort,
6308 LONG aDevice,
6309 IMediumAttachment **aAttachment)
6310{
6311 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6312 aControllerName, aControllerPort, aDevice));
6313
6314 CheckComArgStrNotEmptyOrNull(aControllerName);
6315 CheckComArgOutPointerValid(aAttachment);
6316
6317 AutoCaller autoCaller(this);
6318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6319
6320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6321
6322 *aAttachment = NULL;
6323
6324 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6325 aControllerName,
6326 aControllerPort,
6327 aDevice);
6328 if (pAttach.isNull())
6329 return setError(VBOX_E_OBJECT_NOT_FOUND,
6330 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6331 aDevice, aControllerPort, aControllerName);
6332
6333 pAttach.queryInterfaceTo(aAttachment);
6334
6335 return S_OK;
6336}
6337
6338STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6339 StorageBus_T aConnectionType,
6340 IStorageController **controller)
6341{
6342 CheckComArgStrNotEmptyOrNull(aName);
6343
6344 if ( (aConnectionType <= StorageBus_Null)
6345 || (aConnectionType > StorageBus_SAS))
6346 return setError(E_INVALIDARG,
6347 tr("Invalid connection type: %d"),
6348 aConnectionType);
6349
6350 AutoCaller autoCaller(this);
6351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6352
6353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6354
6355 HRESULT rc = checkStateDependency(MutableStateDep);
6356 if (FAILED(rc)) return rc;
6357
6358 /* try to find one with the name first. */
6359 ComObjPtr<StorageController> ctrl;
6360
6361 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6362 if (SUCCEEDED(rc))
6363 return setError(VBOX_E_OBJECT_IN_USE,
6364 tr("Storage controller named '%ls' already exists"),
6365 aName);
6366
6367 ctrl.createObject();
6368
6369 /* get a new instance number for the storage controller */
6370 ULONG ulInstance = 0;
6371 bool fBootable = true;
6372 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6373 it != mStorageControllers->end();
6374 ++it)
6375 {
6376 if ((*it)->getStorageBus() == aConnectionType)
6377 {
6378 ULONG ulCurInst = (*it)->getInstance();
6379
6380 if (ulCurInst >= ulInstance)
6381 ulInstance = ulCurInst + 1;
6382
6383 /* Only one controller of each type can be marked as bootable. */
6384 if ((*it)->getBootable())
6385 fBootable = false;
6386 }
6387 }
6388
6389 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6390 if (FAILED(rc)) return rc;
6391
6392 setModified(IsModified_Storage);
6393 mStorageControllers.backup();
6394 mStorageControllers->push_back(ctrl);
6395
6396 ctrl.queryInterfaceTo(controller);
6397
6398 /* inform the direct session if any */
6399 alock.release();
6400 onStorageControllerChange();
6401
6402 return S_OK;
6403}
6404
6405STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6406 IStorageController **aStorageController)
6407{
6408 CheckComArgStrNotEmptyOrNull(aName);
6409
6410 AutoCaller autoCaller(this);
6411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6412
6413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 ComObjPtr<StorageController> ctrl;
6416
6417 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6418 if (SUCCEEDED(rc))
6419 ctrl.queryInterfaceTo(aStorageController);
6420
6421 return rc;
6422}
6423
6424STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6425 IStorageController **aStorageController)
6426{
6427 AutoCaller autoCaller(this);
6428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6429
6430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6431
6432 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6433 it != mStorageControllers->end();
6434 ++it)
6435 {
6436 if ((*it)->getInstance() == aInstance)
6437 {
6438 (*it).queryInterfaceTo(aStorageController);
6439 return S_OK;
6440 }
6441 }
6442
6443 return setError(VBOX_E_OBJECT_NOT_FOUND,
6444 tr("Could not find a storage controller with instance number '%lu'"),
6445 aInstance);
6446}
6447
6448STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6449{
6450 AutoCaller autoCaller(this);
6451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6452
6453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6454
6455 HRESULT rc = checkStateDependency(MutableStateDep);
6456 if (FAILED(rc)) return rc;
6457
6458 ComObjPtr<StorageController> ctrl;
6459
6460 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6461 if (SUCCEEDED(rc))
6462 {
6463 /* Ensure that only one controller of each type is marked as bootable. */
6464 if (fBootable == TRUE)
6465 {
6466 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6467 it != mStorageControllers->end();
6468 ++it)
6469 {
6470 ComObjPtr<StorageController> aCtrl = (*it);
6471
6472 if ( (aCtrl->getName() != Utf8Str(aName))
6473 && aCtrl->getBootable() == TRUE
6474 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6475 && aCtrl->getControllerType() == ctrl->getControllerType())
6476 {
6477 aCtrl->setBootable(FALSE);
6478 break;
6479 }
6480 }
6481 }
6482
6483 if (SUCCEEDED(rc))
6484 {
6485 ctrl->setBootable(fBootable);
6486 setModified(IsModified_Storage);
6487 }
6488 }
6489
6490 if (SUCCEEDED(rc))
6491 {
6492 /* inform the direct session if any */
6493 alock.release();
6494 onStorageControllerChange();
6495 }
6496
6497 return rc;
6498}
6499
6500STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6501{
6502 CheckComArgStrNotEmptyOrNull(aName);
6503
6504 AutoCaller autoCaller(this);
6505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6506
6507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 HRESULT rc = checkStateDependency(MutableStateDep);
6510 if (FAILED(rc)) return rc;
6511
6512 ComObjPtr<StorageController> ctrl;
6513 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6514 if (FAILED(rc)) return rc;
6515
6516 {
6517 /* find all attached devices to the appropriate storage controller and detach them all */
6518 // make a temporary list because detachDevice invalidates iterators into
6519 // mMediaData->mAttachments
6520 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6521
6522 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6523 it != llAttachments2.end();
6524 ++it)
6525 {
6526 MediumAttachment *pAttachTemp = *it;
6527
6528 AutoCaller localAutoCaller(pAttachTemp);
6529 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6530
6531 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6532
6533 if (pAttachTemp->getControllerName() == aName)
6534 {
6535 rc = detachDevice(pAttachTemp, alock, NULL);
6536 if (FAILED(rc)) return rc;
6537 }
6538 }
6539 }
6540
6541 /* We can remove it now. */
6542 setModified(IsModified_Storage);
6543 mStorageControllers.backup();
6544
6545 ctrl->unshare();
6546
6547 mStorageControllers->remove(ctrl);
6548
6549 /* inform the direct session if any */
6550 alock.release();
6551 onStorageControllerChange();
6552
6553 return S_OK;
6554}
6555
6556STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6557 ULONG *puOriginX,
6558 ULONG *puOriginY,
6559 ULONG *puWidth,
6560 ULONG *puHeight,
6561 BOOL *pfEnabled)
6562{
6563 LogFlowThisFunc(("\n"));
6564
6565 CheckComArgNotNull(puOriginX);
6566 CheckComArgNotNull(puOriginY);
6567 CheckComArgNotNull(puWidth);
6568 CheckComArgNotNull(puHeight);
6569 CheckComArgNotNull(pfEnabled);
6570
6571 uint32_t u32OriginX= 0;
6572 uint32_t u32OriginY= 0;
6573 uint32_t u32Width = 0;
6574 uint32_t u32Height = 0;
6575 uint16_t u16Flags = 0;
6576
6577 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6578 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6579 if (RT_FAILURE(vrc))
6580 {
6581#ifdef RT_OS_WINDOWS
6582 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6583 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6584 * So just assign fEnable to TRUE again.
6585 * The right fix would be to change GUI API wrappers to make sure that parameters
6586 * are changed only if API succeeds.
6587 */
6588 *pfEnabled = TRUE;
6589#endif
6590 return setError(VBOX_E_IPRT_ERROR,
6591 tr("Saved guest size is not available (%Rrc)"),
6592 vrc);
6593 }
6594
6595 *puOriginX = u32OriginX;
6596 *puOriginY = u32OriginY;
6597 *puWidth = u32Width;
6598 *puHeight = u32Height;
6599 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6600
6601 return S_OK;
6602}
6603
6604STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6605{
6606 LogFlowThisFunc(("\n"));
6607
6608 CheckComArgNotNull(aSize);
6609 CheckComArgNotNull(aWidth);
6610 CheckComArgNotNull(aHeight);
6611
6612 if (aScreenId != 0)
6613 return E_NOTIMPL;
6614
6615 AutoCaller autoCaller(this);
6616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6617
6618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6619
6620 uint8_t *pu8Data = NULL;
6621 uint32_t cbData = 0;
6622 uint32_t u32Width = 0;
6623 uint32_t u32Height = 0;
6624
6625 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6626
6627 if (RT_FAILURE(vrc))
6628 return setError(VBOX_E_IPRT_ERROR,
6629 tr("Saved screenshot data is not available (%Rrc)"),
6630 vrc);
6631
6632 *aSize = cbData;
6633 *aWidth = u32Width;
6634 *aHeight = u32Height;
6635
6636 freeSavedDisplayScreenshot(pu8Data);
6637
6638 return S_OK;
6639}
6640
6641STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6642{
6643 LogFlowThisFunc(("\n"));
6644
6645 CheckComArgNotNull(aWidth);
6646 CheckComArgNotNull(aHeight);
6647 CheckComArgOutSafeArrayPointerValid(aData);
6648
6649 if (aScreenId != 0)
6650 return E_NOTIMPL;
6651
6652 AutoCaller autoCaller(this);
6653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6654
6655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 uint8_t *pu8Data = NULL;
6658 uint32_t cbData = 0;
6659 uint32_t u32Width = 0;
6660 uint32_t u32Height = 0;
6661
6662 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6663
6664 if (RT_FAILURE(vrc))
6665 return setError(VBOX_E_IPRT_ERROR,
6666 tr("Saved screenshot data is not available (%Rrc)"),
6667 vrc);
6668
6669 *aWidth = u32Width;
6670 *aHeight = u32Height;
6671
6672 com::SafeArray<BYTE> bitmap(cbData);
6673 /* Convert pixels to format expected by the API caller. */
6674 if (aBGR)
6675 {
6676 /* [0] B, [1] G, [2] R, [3] A. */
6677 for (unsigned i = 0; i < cbData; i += 4)
6678 {
6679 bitmap[i] = pu8Data[i];
6680 bitmap[i + 1] = pu8Data[i + 1];
6681 bitmap[i + 2] = pu8Data[i + 2];
6682 bitmap[i + 3] = 0xff;
6683 }
6684 }
6685 else
6686 {
6687 /* [0] R, [1] G, [2] B, [3] A. */
6688 for (unsigned i = 0; i < cbData; i += 4)
6689 {
6690 bitmap[i] = pu8Data[i + 2];
6691 bitmap[i + 1] = pu8Data[i + 1];
6692 bitmap[i + 2] = pu8Data[i];
6693 bitmap[i + 3] = 0xff;
6694 }
6695 }
6696 bitmap.detachTo(ComSafeArrayOutArg(aData));
6697
6698 freeSavedDisplayScreenshot(pu8Data);
6699
6700 return S_OK;
6701}
6702
6703
6704STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6705{
6706 LogFlowThisFunc(("\n"));
6707
6708 CheckComArgNotNull(aWidth);
6709 CheckComArgNotNull(aHeight);
6710 CheckComArgOutSafeArrayPointerValid(aData);
6711
6712 if (aScreenId != 0)
6713 return E_NOTIMPL;
6714
6715 AutoCaller autoCaller(this);
6716 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6717
6718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6719
6720 uint8_t *pu8Data = NULL;
6721 uint32_t cbData = 0;
6722 uint32_t u32Width = 0;
6723 uint32_t u32Height = 0;
6724
6725 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6726
6727 if (RT_FAILURE(vrc))
6728 return setError(VBOX_E_IPRT_ERROR,
6729 tr("Saved screenshot data is not available (%Rrc)"),
6730 vrc);
6731
6732 *aWidth = u32Width;
6733 *aHeight = u32Height;
6734
6735 HRESULT rc = S_OK;
6736 uint8_t *pu8PNG = NULL;
6737 uint32_t cbPNG = 0;
6738 uint32_t cxPNG = 0;
6739 uint32_t cyPNG = 0;
6740
6741 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6742
6743 if (RT_SUCCESS(vrc))
6744 {
6745 com::SafeArray<BYTE> screenData(cbPNG);
6746 screenData.initFrom(pu8PNG, cbPNG);
6747 if (pu8PNG)
6748 RTMemFree(pu8PNG);
6749 screenData.detachTo(ComSafeArrayOutArg(aData));
6750 }
6751 else
6752 {
6753 if (pu8PNG)
6754 RTMemFree(pu8PNG);
6755 return setError(VBOX_E_IPRT_ERROR,
6756 tr("Could not convert screenshot to PNG (%Rrc)"),
6757 vrc);
6758 }
6759
6760 freeSavedDisplayScreenshot(pu8Data);
6761
6762 return rc;
6763}
6764
6765STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6766{
6767 LogFlowThisFunc(("\n"));
6768
6769 CheckComArgNotNull(aSize);
6770 CheckComArgNotNull(aWidth);
6771 CheckComArgNotNull(aHeight);
6772
6773 if (aScreenId != 0)
6774 return E_NOTIMPL;
6775
6776 AutoCaller autoCaller(this);
6777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6778
6779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6780
6781 uint8_t *pu8Data = NULL;
6782 uint32_t cbData = 0;
6783 uint32_t u32Width = 0;
6784 uint32_t u32Height = 0;
6785
6786 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6787
6788 if (RT_FAILURE(vrc))
6789 return setError(VBOX_E_IPRT_ERROR,
6790 tr("Saved screenshot data is not available (%Rrc)"),
6791 vrc);
6792
6793 *aSize = cbData;
6794 *aWidth = u32Width;
6795 *aHeight = u32Height;
6796
6797 freeSavedDisplayScreenshot(pu8Data);
6798
6799 return S_OK;
6800}
6801
6802STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6803{
6804 LogFlowThisFunc(("\n"));
6805
6806 CheckComArgNotNull(aWidth);
6807 CheckComArgNotNull(aHeight);
6808 CheckComArgOutSafeArrayPointerValid(aData);
6809
6810 if (aScreenId != 0)
6811 return E_NOTIMPL;
6812
6813 AutoCaller autoCaller(this);
6814 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6815
6816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6817
6818 uint8_t *pu8Data = NULL;
6819 uint32_t cbData = 0;
6820 uint32_t u32Width = 0;
6821 uint32_t u32Height = 0;
6822
6823 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6824
6825 if (RT_FAILURE(vrc))
6826 return setError(VBOX_E_IPRT_ERROR,
6827 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6828 vrc);
6829
6830 *aWidth = u32Width;
6831 *aHeight = u32Height;
6832
6833 com::SafeArray<BYTE> png(cbData);
6834 png.initFrom(pu8Data, cbData);
6835 png.detachTo(ComSafeArrayOutArg(aData));
6836
6837 freeSavedDisplayScreenshot(pu8Data);
6838
6839 return S_OK;
6840}
6841
6842STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6843{
6844 HRESULT rc = S_OK;
6845 LogFlowThisFunc(("\n"));
6846
6847 AutoCaller autoCaller(this);
6848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6849
6850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6851
6852 if (!mHWData->mCPUHotPlugEnabled)
6853 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6854
6855 if (aCpu >= mHWData->mCPUCount)
6856 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6857
6858 if (mHWData->mCPUAttached[aCpu])
6859 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6860
6861 alock.release();
6862 rc = onCPUChange(aCpu, false);
6863 alock.acquire();
6864 if (FAILED(rc)) return rc;
6865
6866 setModified(IsModified_MachineData);
6867 mHWData.backup();
6868 mHWData->mCPUAttached[aCpu] = true;
6869
6870 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6871 if (Global::IsOnline(mData->mMachineState))
6872 saveSettings(NULL);
6873
6874 return S_OK;
6875}
6876
6877STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6878{
6879 HRESULT rc = S_OK;
6880 LogFlowThisFunc(("\n"));
6881
6882 AutoCaller autoCaller(this);
6883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6884
6885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 if (!mHWData->mCPUHotPlugEnabled)
6888 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6889
6890 if (aCpu >= SchemaDefs::MaxCPUCount)
6891 return setError(E_INVALIDARG,
6892 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6893 SchemaDefs::MaxCPUCount);
6894
6895 if (!mHWData->mCPUAttached[aCpu])
6896 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6897
6898 /* CPU 0 can't be detached */
6899 if (aCpu == 0)
6900 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6901
6902 alock.release();
6903 rc = onCPUChange(aCpu, true);
6904 alock.acquire();
6905 if (FAILED(rc)) return rc;
6906
6907 setModified(IsModified_MachineData);
6908 mHWData.backup();
6909 mHWData->mCPUAttached[aCpu] = false;
6910
6911 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6912 if (Global::IsOnline(mData->mMachineState))
6913 saveSettings(NULL);
6914
6915 return S_OK;
6916}
6917
6918STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6919{
6920 LogFlowThisFunc(("\n"));
6921
6922 CheckComArgNotNull(aCpuAttached);
6923
6924 *aCpuAttached = false;
6925
6926 AutoCaller autoCaller(this);
6927 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6928
6929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6930
6931 /* If hotplug is enabled the CPU is always enabled. */
6932 if (!mHWData->mCPUHotPlugEnabled)
6933 {
6934 if (aCpu < mHWData->mCPUCount)
6935 *aCpuAttached = true;
6936 }
6937 else
6938 {
6939 if (aCpu < SchemaDefs::MaxCPUCount)
6940 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6941 }
6942
6943 return S_OK;
6944}
6945
6946STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6947{
6948 CheckComArgOutPointerValid(aName);
6949
6950 AutoCaller autoCaller(this);
6951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6952
6953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6954
6955 Utf8Str log = queryLogFilename(aIdx);
6956 if (!RTFileExists(log.c_str()))
6957 log.setNull();
6958 log.cloneTo(aName);
6959
6960 return S_OK;
6961}
6962
6963STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6964{
6965 LogFlowThisFunc(("\n"));
6966 CheckComArgOutSafeArrayPointerValid(aData);
6967 if (aSize < 0)
6968 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6969
6970 AutoCaller autoCaller(this);
6971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6972
6973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6974
6975 HRESULT rc = S_OK;
6976 Utf8Str log = queryLogFilename(aIdx);
6977
6978 /* do not unnecessarily hold the lock while doing something which does
6979 * not need the lock and potentially takes a long time. */
6980 alock.release();
6981
6982 /* Limit the chunk size to 32K for now, as that gives better performance
6983 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6984 * One byte expands to approx. 25 bytes of breathtaking XML. */
6985 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6986 com::SafeArray<BYTE> logData(cbData);
6987
6988 RTFILE LogFile;
6989 int vrc = RTFileOpen(&LogFile, log.c_str(),
6990 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6991 if (RT_SUCCESS(vrc))
6992 {
6993 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6994 if (RT_SUCCESS(vrc))
6995 logData.resize(cbData);
6996 else
6997 rc = setError(VBOX_E_IPRT_ERROR,
6998 tr("Could not read log file '%s' (%Rrc)"),
6999 log.c_str(), vrc);
7000 RTFileClose(LogFile);
7001 }
7002 else
7003 rc = setError(VBOX_E_IPRT_ERROR,
7004 tr("Could not open log file '%s' (%Rrc)"),
7005 log.c_str(), vrc);
7006
7007 if (FAILED(rc))
7008 logData.resize(0);
7009 logData.detachTo(ComSafeArrayOutArg(aData));
7010
7011 return rc;
7012}
7013
7014
7015/**
7016 * Currently this method doesn't attach device to the running VM,
7017 * just makes sure it's plugged on next VM start.
7018 */
7019STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
7020{
7021 AutoCaller autoCaller(this);
7022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7023
7024 // lock scope
7025 {
7026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7027
7028 HRESULT rc = checkStateDependency(MutableStateDep);
7029 if (FAILED(rc)) return rc;
7030
7031 ChipsetType_T aChipset = ChipsetType_PIIX3;
7032 COMGETTER(ChipsetType)(&aChipset);
7033
7034 if (aChipset != ChipsetType_ICH9)
7035 {
7036 return setError(E_INVALIDARG,
7037 tr("Host PCI attachment only supported with ICH9 chipset"));
7038 }
7039
7040 // check if device with this host PCI address already attached
7041 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7042 it != mHWData->mPCIDeviceAssignments.end();
7043 ++it)
7044 {
7045 LONG iHostAddress = -1;
7046 ComPtr<PCIDeviceAttachment> pAttach;
7047 pAttach = *it;
7048 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7049 if (iHostAddress == hostAddress)
7050 return setError(E_INVALIDARG,
7051 tr("Device with host PCI address already attached to this VM"));
7052 }
7053
7054 ComObjPtr<PCIDeviceAttachment> pda;
7055 char name[32];
7056
7057 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
7058 Bstr bname(name);
7059 pda.createObject();
7060 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
7061 setModified(IsModified_MachineData);
7062 mHWData.backup();
7063 mHWData->mPCIDeviceAssignments.push_back(pda);
7064 }
7065
7066 return S_OK;
7067}
7068
7069/**
7070 * Currently this method doesn't detach device from the running VM,
7071 * just makes sure it's not plugged on next VM start.
7072 */
7073STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
7074{
7075 AutoCaller autoCaller(this);
7076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7077
7078 ComObjPtr<PCIDeviceAttachment> pAttach;
7079 bool fRemoved = false;
7080 HRESULT rc;
7081
7082 // lock scope
7083 {
7084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7085
7086 rc = checkStateDependency(MutableStateDep);
7087 if (FAILED(rc)) return rc;
7088
7089 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
7090 it != mHWData->mPCIDeviceAssignments.end();
7091 ++it)
7092 {
7093 LONG iHostAddress = -1;
7094 pAttach = *it;
7095 pAttach->COMGETTER(HostAddress)(&iHostAddress);
7096 if (iHostAddress != -1 && iHostAddress == hostAddress)
7097 {
7098 setModified(IsModified_MachineData);
7099 mHWData.backup();
7100 mHWData->mPCIDeviceAssignments.remove(pAttach);
7101 fRemoved = true;
7102 break;
7103 }
7104 }
7105 }
7106
7107
7108 /* Fire event outside of the lock */
7109 if (fRemoved)
7110 {
7111 Assert(!pAttach.isNull());
7112 ComPtr<IEventSource> es;
7113 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
7114 Assert(SUCCEEDED(rc));
7115 Bstr mid;
7116 rc = this->COMGETTER(Id)(mid.asOutParam());
7117 Assert(SUCCEEDED(rc));
7118 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
7119 }
7120
7121 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
7122 tr("No host PCI device %08x attached"),
7123 hostAddress
7124 );
7125}
7126
7127STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
7128{
7129 CheckComArgOutSafeArrayPointerValid(aAssignments);
7130
7131 AutoCaller autoCaller(this);
7132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7133
7134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7135
7136 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
7137 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
7138
7139 return S_OK;
7140}
7141
7142STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
7143{
7144 CheckComArgOutPointerValid(aBandwidthControl);
7145
7146 AutoCaller autoCaller(this);
7147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7148
7149 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
7150
7151 return S_OK;
7152}
7153
7154STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
7155{
7156 CheckComArgOutPointerValid(pfEnabled);
7157 AutoCaller autoCaller(this);
7158 HRESULT hrc = autoCaller.rc();
7159 if (SUCCEEDED(hrc))
7160 {
7161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7162 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
7163 }
7164 return hrc;
7165}
7166
7167STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
7168{
7169 AutoCaller autoCaller(this);
7170 HRESULT hrc = autoCaller.rc();
7171 if (SUCCEEDED(hrc))
7172 {
7173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7174 hrc = checkStateDependency(MutableStateDep);
7175 if (SUCCEEDED(hrc))
7176 {
7177 hrc = mHWData.backupEx();
7178 if (SUCCEEDED(hrc))
7179 {
7180 setModified(IsModified_MachineData);
7181 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
7182 }
7183 }
7184 }
7185 return hrc;
7186}
7187
7188STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
7189{
7190 CheckComArgOutPointerValid(pbstrConfig);
7191 AutoCaller autoCaller(this);
7192 HRESULT hrc = autoCaller.rc();
7193 if (SUCCEEDED(hrc))
7194 {
7195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7196 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
7197 }
7198 return hrc;
7199}
7200
7201STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
7202{
7203 CheckComArgStr(bstrConfig);
7204 AutoCaller autoCaller(this);
7205 HRESULT hrc = autoCaller.rc();
7206 if (SUCCEEDED(hrc))
7207 {
7208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7209 hrc = checkStateDependency(MutableStateDep);
7210 if (SUCCEEDED(hrc))
7211 {
7212 hrc = mHWData.backupEx();
7213 if (SUCCEEDED(hrc))
7214 {
7215 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
7216 if (SUCCEEDED(hrc))
7217 setModified(IsModified_MachineData);
7218 }
7219 }
7220 }
7221 return hrc;
7222
7223}
7224
7225STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
7226{
7227 CheckComArgOutPointerValid(pfAllow);
7228 AutoCaller autoCaller(this);
7229 HRESULT hrc = autoCaller.rc();
7230 if (SUCCEEDED(hrc))
7231 {
7232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7233 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
7234 }
7235 return hrc;
7236}
7237
7238STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
7239{
7240 AutoCaller autoCaller(this);
7241 HRESULT hrc = autoCaller.rc();
7242 if (SUCCEEDED(hrc))
7243 {
7244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7245 hrc = checkStateDependency(MutableStateDep);
7246 if (SUCCEEDED(hrc))
7247 {
7248 hrc = mHWData.backupEx();
7249 if (SUCCEEDED(hrc))
7250 {
7251 setModified(IsModified_MachineData);
7252 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
7253 }
7254 }
7255 }
7256 return hrc;
7257}
7258
7259STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
7260{
7261 CheckComArgOutPointerValid(pfEnabled);
7262 AutoCaller autoCaller(this);
7263 HRESULT hrc = autoCaller.rc();
7264 if (SUCCEEDED(hrc))
7265 {
7266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7267 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
7268 }
7269 return hrc;
7270}
7271
7272STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
7273{
7274 AutoCaller autoCaller(this);
7275 HRESULT hrc = autoCaller.rc();
7276 if (SUCCEEDED(hrc))
7277 {
7278 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7279 hrc = checkStateDependency(MutableStateDep);
7280 if ( SUCCEEDED(hrc)
7281 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7282 {
7283 AutostartDb *autostartDb = mParent->getAutostartDb();
7284 int vrc;
7285
7286 if (fEnabled)
7287 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7288 else
7289 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7290
7291 if (RT_SUCCESS(vrc))
7292 {
7293 hrc = mHWData.backupEx();
7294 if (SUCCEEDED(hrc))
7295 {
7296 setModified(IsModified_MachineData);
7297 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7298 }
7299 }
7300 else if (vrc == VERR_NOT_SUPPORTED)
7301 hrc = setError(VBOX_E_NOT_SUPPORTED,
7302 tr("The VM autostart feature is not supported on this platform"));
7303 else if (vrc == VERR_PATH_NOT_FOUND)
7304 hrc = setError(E_FAIL,
7305 tr("The path to the autostart database is not set"));
7306 else
7307 hrc = setError(E_UNEXPECTED,
7308 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7309 fEnabled ? "Adding" : "Removing",
7310 mUserData->s.strName.c_str(), vrc);
7311 }
7312 }
7313 return hrc;
7314}
7315
7316STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7317{
7318 CheckComArgOutPointerValid(puDelay);
7319 AutoCaller autoCaller(this);
7320 HRESULT hrc = autoCaller.rc();
7321 if (SUCCEEDED(hrc))
7322 {
7323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7324 *puDelay = mHWData->mAutostart.uAutostartDelay;
7325 }
7326 return hrc;
7327}
7328
7329STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7330{
7331 AutoCaller autoCaller(this);
7332 HRESULT hrc = autoCaller.rc();
7333 if (SUCCEEDED(hrc))
7334 {
7335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7336 hrc = checkStateDependency(MutableStateDep);
7337 if (SUCCEEDED(hrc))
7338 {
7339 hrc = mHWData.backupEx();
7340 if (SUCCEEDED(hrc))
7341 {
7342 setModified(IsModified_MachineData);
7343 mHWData->mAutostart.uAutostartDelay = uDelay;
7344 }
7345 }
7346 }
7347 return hrc;
7348}
7349
7350STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7351{
7352 CheckComArgOutPointerValid(penmAutostopType);
7353 AutoCaller autoCaller(this);
7354 HRESULT hrc = autoCaller.rc();
7355 if (SUCCEEDED(hrc))
7356 {
7357 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7358 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7359 }
7360 return hrc;
7361}
7362
7363STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7364{
7365 AutoCaller autoCaller(this);
7366 HRESULT hrc = autoCaller.rc();
7367 if (SUCCEEDED(hrc))
7368 {
7369 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7370 hrc = checkStateDependency(MutableStateDep);
7371 if ( SUCCEEDED(hrc)
7372 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7373 {
7374 AutostartDb *autostartDb = mParent->getAutostartDb();
7375 int vrc;
7376
7377 if (enmAutostopType != AutostopType_Disabled)
7378 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7379 else
7380 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7381
7382 if (RT_SUCCESS(vrc))
7383 {
7384 hrc = mHWData.backupEx();
7385 if (SUCCEEDED(hrc))
7386 {
7387 setModified(IsModified_MachineData);
7388 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7389 }
7390 }
7391 else if (vrc == VERR_NOT_SUPPORTED)
7392 hrc = setError(VBOX_E_NOT_SUPPORTED,
7393 tr("The VM autostop feature is not supported on this platform"));
7394 else if (vrc == VERR_PATH_NOT_FOUND)
7395 hrc = setError(E_FAIL,
7396 tr("The path to the autostart database is not set"));
7397 else
7398 hrc = setError(E_UNEXPECTED,
7399 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7400 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7401 mUserData->s.strName.c_str(), vrc);
7402 }
7403 }
7404 return hrc;
7405}
7406
7407STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7408{
7409 CheckComArgOutPointerValid(aDefaultFrontend);
7410 AutoCaller autoCaller(this);
7411 HRESULT hrc = autoCaller.rc();
7412 if (SUCCEEDED(hrc))
7413 {
7414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7415 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7416 }
7417 return hrc;
7418}
7419
7420STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7421{
7422 CheckComArgStr(aDefaultFrontend);
7423 AutoCaller autoCaller(this);
7424 HRESULT hrc = autoCaller.rc();
7425 if (SUCCEEDED(hrc))
7426 {
7427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7428 hrc = checkStateDependency(MutableOrSavedStateDep);
7429 if (SUCCEEDED(hrc))
7430 {
7431 hrc = mHWData.backupEx();
7432 if (SUCCEEDED(hrc))
7433 {
7434 setModified(IsModified_MachineData);
7435 mHWData->mDefaultFrontend = aDefaultFrontend;
7436 }
7437 }
7438 }
7439 return hrc;
7440}
7441
7442STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon))
7443{
7444 CheckComArgSafeArrayNotNull(aIcon);
7445 CheckComArgOutSafeArrayPointerValid(aIcon);
7446 AutoCaller autoCaller(this);
7447 HRESULT hrc = autoCaller.rc();
7448 if (SUCCEEDED(hrc))
7449 {
7450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7451 com::SafeArray<BYTE> icon(mUserData->mIcon.size());
7452 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size());
7453 icon.detachTo(ComSafeArrayOutArg(aIcon));
7454 }
7455 return hrc;
7456}
7457
7458STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon))
7459{
7460 CheckComArgSafeArrayNotNull(aIcon);
7461 AutoCaller autoCaller(this);
7462 HRESULT hrc = autoCaller.rc();
7463 if (SUCCEEDED(hrc))
7464 {
7465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7466 hrc = checkStateDependency(MutableOrSavedStateDep);
7467 if (SUCCEEDED(hrc))
7468 {
7469 setModified(IsModified_MachineData);
7470 mUserData.backup();
7471 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon));
7472 mUserData->mIcon.clear();
7473 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size());
7474 }
7475 }
7476 return hrc;
7477}
7478
7479STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7480{
7481 LogFlowFuncEnter();
7482
7483 CheckComArgNotNull(pTarget);
7484 CheckComArgOutPointerValid(pProgress);
7485
7486 /* Convert the options. */
7487 RTCList<CloneOptions_T> optList;
7488 if (options != NULL)
7489 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7490
7491 if (optList.contains(CloneOptions_Link))
7492 {
7493 if (!isSnapshotMachine())
7494 return setError(E_INVALIDARG,
7495 tr("Linked clone can only be created from a snapshot"));
7496 if (mode != CloneMode_MachineState)
7497 return setError(E_INVALIDARG,
7498 tr("Linked clone can only be created for a single machine state"));
7499 }
7500 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7501
7502 AutoCaller autoCaller(this);
7503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7504
7505
7506 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7507
7508 HRESULT rc = pWorker->start(pProgress);
7509
7510 LogFlowFuncLeave();
7511
7512 return rc;
7513}
7514
7515// public methods for internal purposes
7516/////////////////////////////////////////////////////////////////////////////
7517
7518/**
7519 * Adds the given IsModified_* flag to the dirty flags of the machine.
7520 * This must be called either during loadSettings or under the machine write lock.
7521 * @param fl
7522 */
7523void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7524{
7525 mData->flModifications |= fl;
7526 if (fAllowStateModification && isStateModificationAllowed())
7527 mData->mCurrentStateModified = true;
7528}
7529
7530/**
7531 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7532 * care of the write locking.
7533 *
7534 * @param fModifications The flag to add.
7535 */
7536void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7537{
7538 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7539 setModified(fModification, fAllowStateModification);
7540}
7541
7542/**
7543 * Saves the registry entry of this machine to the given configuration node.
7544 *
7545 * @param aEntryNode Node to save the registry entry to.
7546 *
7547 * @note locks this object for reading.
7548 */
7549HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7550{
7551 AutoLimitedCaller autoCaller(this);
7552 AssertComRCReturnRC(autoCaller.rc());
7553
7554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7555
7556 data.uuid = mData->mUuid;
7557 data.strSettingsFile = mData->m_strConfigFile;
7558
7559 return S_OK;
7560}
7561
7562/**
7563 * Calculates the absolute path of the given path taking the directory of the
7564 * machine settings file as the current directory.
7565 *
7566 * @param aPath Path to calculate the absolute path for.
7567 * @param aResult Where to put the result (used only on success, can be the
7568 * same Utf8Str instance as passed in @a aPath).
7569 * @return IPRT result.
7570 *
7571 * @note Locks this object for reading.
7572 */
7573int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7574{
7575 AutoCaller autoCaller(this);
7576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7577
7578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7579
7580 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7581
7582 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7583
7584 strSettingsDir.stripFilename();
7585 char folder[RTPATH_MAX];
7586 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7587 if (RT_SUCCESS(vrc))
7588 aResult = folder;
7589
7590 return vrc;
7591}
7592
7593/**
7594 * Copies strSource to strTarget, making it relative to the machine folder
7595 * if it is a subdirectory thereof, or simply copying it otherwise.
7596 *
7597 * @param strSource Path to evaluate and copy.
7598 * @param strTarget Buffer to receive target path.
7599 *
7600 * @note Locks this object for reading.
7601 */
7602void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7603 Utf8Str &strTarget)
7604{
7605 AutoCaller autoCaller(this);
7606 AssertComRCReturn(autoCaller.rc(), (void)0);
7607
7608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7609
7610 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7611 // use strTarget as a temporary buffer to hold the machine settings dir
7612 strTarget = mData->m_strConfigFileFull;
7613 strTarget.stripFilename();
7614 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7615 {
7616 // is relative: then append what's left
7617 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7618 // for empty paths (only possible for subdirs) use "." to avoid
7619 // triggering default settings for not present config attributes.
7620 if (strTarget.isEmpty())
7621 strTarget = ".";
7622 }
7623 else
7624 // is not relative: then overwrite
7625 strTarget = strSource;
7626}
7627
7628/**
7629 * Returns the full path to the machine's log folder in the
7630 * \a aLogFolder argument.
7631 */
7632void Machine::getLogFolder(Utf8Str &aLogFolder)
7633{
7634 AutoCaller autoCaller(this);
7635 AssertComRCReturnVoid(autoCaller.rc());
7636
7637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7638
7639 char szTmp[RTPATH_MAX];
7640 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7641 if (RT_SUCCESS(vrc))
7642 {
7643 if (szTmp[0] && !mUserData.isNull())
7644 {
7645 char szTmp2[RTPATH_MAX];
7646 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7647 if (RT_SUCCESS(vrc))
7648 aLogFolder = BstrFmt("%s%c%s",
7649 szTmp2,
7650 RTPATH_DELIMITER,
7651 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7652 }
7653 else
7654 vrc = VERR_PATH_IS_RELATIVE;
7655 }
7656
7657 if (RT_FAILURE(vrc))
7658 {
7659 // fallback if VBOX_USER_LOGHOME is not set or invalid
7660 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7661 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7662 aLogFolder.append(RTPATH_DELIMITER);
7663 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7664 }
7665}
7666
7667/**
7668 * Returns the full path to the machine's log file for an given index.
7669 */
7670Utf8Str Machine::queryLogFilename(ULONG idx)
7671{
7672 Utf8Str logFolder;
7673 getLogFolder(logFolder);
7674 Assert(logFolder.length());
7675 Utf8Str log;
7676 if (idx == 0)
7677 log = Utf8StrFmt("%s%cVBox.log",
7678 logFolder.c_str(), RTPATH_DELIMITER);
7679 else
7680 log = Utf8StrFmt("%s%cVBox.log.%d",
7681 logFolder.c_str(), RTPATH_DELIMITER, idx);
7682 return log;
7683}
7684
7685/**
7686 * Composes a unique saved state filename based on the current system time. The filename is
7687 * granular to the second so this will work so long as no more than one snapshot is taken on
7688 * a machine per second.
7689 *
7690 * Before version 4.1, we used this formula for saved state files:
7691 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7692 * which no longer works because saved state files can now be shared between the saved state of the
7693 * "saved" machine and an online snapshot, and the following would cause problems:
7694 * 1) save machine
7695 * 2) create online snapshot from that machine state --> reusing saved state file
7696 * 3) save machine again --> filename would be reused, breaking the online snapshot
7697 *
7698 * So instead we now use a timestamp.
7699 *
7700 * @param str
7701 */
7702void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7703{
7704 AutoCaller autoCaller(this);
7705 AssertComRCReturnVoid(autoCaller.rc());
7706
7707 {
7708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7709 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7710 }
7711
7712 RTTIMESPEC ts;
7713 RTTimeNow(&ts);
7714 RTTIME time;
7715 RTTimeExplode(&time, &ts);
7716
7717 strStateFilePath += RTPATH_DELIMITER;
7718 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7719 time.i32Year, time.u8Month, time.u8MonthDay,
7720 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7721}
7722
7723/**
7724 * Returns the full path to the default video capture file.
7725 */
7726void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile)
7727{
7728 AutoCaller autoCaller(this);
7729 AssertComRCReturnVoid(autoCaller.rc());
7730
7731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7732
7733 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7734 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname
7735 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7736}
7737
7738/**
7739 * Returns whether at least one USB controller is present for the VM.
7740 */
7741bool Machine::isUSBControllerPresent()
7742{
7743 AutoCaller autoCaller(this);
7744 AssertComRCReturn(autoCaller.rc(), false);
7745
7746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7747
7748 BOOL fEnabled = FALSE;
7749 HRESULT rc = mUSBController->COMGETTER(Enabled)(&fEnabled);
7750 if (SUCCEEDED(rc))
7751 return !!fEnabled;
7752 else
7753 return false;
7754}
7755
7756/**
7757 * @note Locks this object for writing, calls the client process
7758 * (inside the lock).
7759 */
7760HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7761 const Utf8Str &strFrontend,
7762 const Utf8Str &strEnvironment,
7763 ProgressProxy *aProgress)
7764{
7765 LogFlowThisFuncEnter();
7766
7767 AssertReturn(aControl, E_FAIL);
7768 AssertReturn(aProgress, E_FAIL);
7769 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7770
7771 AutoCaller autoCaller(this);
7772 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7773
7774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7775
7776 if (!mData->mRegistered)
7777 return setError(E_UNEXPECTED,
7778 tr("The machine '%s' is not registered"),
7779 mUserData->s.strName.c_str());
7780
7781 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7782
7783 if ( mData->mSession.mState == SessionState_Locked
7784 || mData->mSession.mState == SessionState_Spawning
7785 || mData->mSession.mState == SessionState_Unlocking)
7786 return setError(VBOX_E_INVALID_OBJECT_STATE,
7787 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7788 mUserData->s.strName.c_str());
7789
7790 /* may not be busy */
7791 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7792
7793 /* get the path to the executable */
7794 char szPath[RTPATH_MAX];
7795 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7796 size_t sz = strlen(szPath);
7797 szPath[sz++] = RTPATH_DELIMITER;
7798 szPath[sz] = 0;
7799 char *cmd = szPath + sz;
7800 sz = RTPATH_MAX - sz;
7801
7802 int vrc = VINF_SUCCESS;
7803 RTPROCESS pid = NIL_RTPROCESS;
7804
7805 RTENV env = RTENV_DEFAULT;
7806
7807 if (!strEnvironment.isEmpty())
7808 {
7809 char *newEnvStr = NULL;
7810
7811 do
7812 {
7813 /* clone the current environment */
7814 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7815 AssertRCBreakStmt(vrc2, vrc = vrc2);
7816
7817 newEnvStr = RTStrDup(strEnvironment.c_str());
7818 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7819
7820 /* put new variables to the environment
7821 * (ignore empty variable names here since RTEnv API
7822 * intentionally doesn't do that) */
7823 char *var = newEnvStr;
7824 for (char *p = newEnvStr; *p; ++p)
7825 {
7826 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7827 {
7828 *p = '\0';
7829 if (*var)
7830 {
7831 char *val = strchr(var, '=');
7832 if (val)
7833 {
7834 *val++ = '\0';
7835 vrc2 = RTEnvSetEx(env, var, val);
7836 }
7837 else
7838 vrc2 = RTEnvUnsetEx(env, var);
7839 if (RT_FAILURE(vrc2))
7840 break;
7841 }
7842 var = p + 1;
7843 }
7844 }
7845 if (RT_SUCCESS(vrc2) && *var)
7846 vrc2 = RTEnvPutEx(env, var);
7847
7848 AssertRCBreakStmt(vrc2, vrc = vrc2);
7849 }
7850 while (0);
7851
7852 if (newEnvStr != NULL)
7853 RTStrFree(newEnvStr);
7854 }
7855
7856#ifdef VBOX_WITH_QTGUI
7857 if (strFrontend == "gui" || strFrontend == "GUI/Qt")
7858 {
7859# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7860 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7861# else
7862 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7863# endif
7864 Assert(sz >= sizeof(VirtualBox_exe));
7865 strcpy(cmd, VirtualBox_exe);
7866
7867 Utf8Str idStr = mData->mUuid.toString();
7868 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7869 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7870 }
7871#else /* !VBOX_WITH_QTGUI */
7872 if (0)
7873 ;
7874#endif /* VBOX_WITH_QTGUI */
7875
7876 else
7877
7878#ifdef VBOX_WITH_VBOXSDL
7879 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7880 {
7881 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7882 Assert(sz >= sizeof(VBoxSDL_exe));
7883 strcpy(cmd, VBoxSDL_exe);
7884
7885 Utf8Str idStr = mData->mUuid.toString();
7886 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7887 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7888 }
7889#else /* !VBOX_WITH_VBOXSDL */
7890 if (0)
7891 ;
7892#endif /* !VBOX_WITH_VBOXSDL */
7893
7894 else
7895
7896#ifdef VBOX_WITH_HEADLESS
7897 if ( strFrontend == "headless"
7898 || strFrontend == "capture"
7899 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7900 )
7901 {
7902 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7903 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7904 * and a VM works even if the server has not been installed.
7905 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7906 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7907 * differently in 4.0 and 3.x.
7908 */
7909 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7910 Assert(sz >= sizeof(VBoxHeadless_exe));
7911 strcpy(cmd, VBoxHeadless_exe);
7912
7913 Utf8Str idStr = mData->mUuid.toString();
7914 /* Leave space for "--capture" arg. */
7915 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7916 "--startvm", idStr.c_str(),
7917 "--vrde", "config",
7918 0, /* For "--capture". */
7919 0 };
7920 if (strFrontend == "capture")
7921 {
7922 unsigned pos = RT_ELEMENTS(args) - 2;
7923 args[pos] = "--capture";
7924 }
7925 vrc = RTProcCreate(szPath, args, env,
7926#ifdef RT_OS_WINDOWS
7927 RTPROC_FLAGS_NO_WINDOW
7928#else
7929 0
7930#endif
7931 , &pid);
7932 }
7933#else /* !VBOX_WITH_HEADLESS */
7934 if (0)
7935 ;
7936#endif /* !VBOX_WITH_HEADLESS */
7937 else
7938 {
7939 RTEnvDestroy(env);
7940 return setError(E_INVALIDARG,
7941 tr("Invalid frontend name: '%s'"),
7942 strFrontend.c_str());
7943 }
7944
7945 RTEnvDestroy(env);
7946
7947 if (RT_FAILURE(vrc))
7948 return setError(VBOX_E_IPRT_ERROR,
7949 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7950 mUserData->s.strName.c_str(), vrc);
7951
7952 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7953
7954 /*
7955 * Note that we don't release the lock here before calling the client,
7956 * because it doesn't need to call us back if called with a NULL argument.
7957 * Releasing the lock here is dangerous because we didn't prepare the
7958 * launch data yet, but the client we've just started may happen to be
7959 * too fast and call openSession() that will fail (because of PID, etc.),
7960 * so that the Machine will never get out of the Spawning session state.
7961 */
7962
7963 /* inform the session that it will be a remote one */
7964 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7965 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7966 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7967
7968 if (FAILED(rc))
7969 {
7970 /* restore the session state */
7971 mData->mSession.mState = SessionState_Unlocked;
7972 /* The failure may occur w/o any error info (from RPC), so provide one */
7973 return setError(VBOX_E_VM_ERROR,
7974 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7975 }
7976
7977 /* attach launch data to the machine */
7978 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7979 mData->mSession.mRemoteControls.push_back(aControl);
7980 mData->mSession.mProgress = aProgress;
7981 mData->mSession.mPID = pid;
7982 mData->mSession.mState = SessionState_Spawning;
7983 mData->mSession.mType = strFrontend;
7984
7985 LogFlowThisFuncLeave();
7986 return S_OK;
7987}
7988
7989/**
7990 * Returns @c true if the given machine has an open direct session and returns
7991 * the session machine instance and additional session data (on some platforms)
7992 * if so.
7993 *
7994 * Note that when the method returns @c false, the arguments remain unchanged.
7995 *
7996 * @param aMachine Session machine object.
7997 * @param aControl Direct session control object (optional).
7998 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7999 *
8000 * @note locks this object for reading.
8001 */
8002#if defined(RT_OS_WINDOWS)
8003bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8004 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8005 HANDLE *aIPCSem /*= NULL*/,
8006 bool aAllowClosing /*= false*/)
8007#elif defined(RT_OS_OS2)
8008bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8009 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8010 HMTX *aIPCSem /*= NULL*/,
8011 bool aAllowClosing /*= false*/)
8012#else
8013bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8014 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8015 bool aAllowClosing /*= false*/)
8016#endif
8017{
8018 AutoLimitedCaller autoCaller(this);
8019 AssertComRCReturn(autoCaller.rc(), false);
8020
8021 /* just return false for inaccessible machines */
8022 if (autoCaller.state() != Ready)
8023 return false;
8024
8025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8026
8027 if ( mData->mSession.mState == SessionState_Locked
8028 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8029 )
8030 {
8031 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8032
8033 aMachine = mData->mSession.mMachine;
8034
8035 if (aControl != NULL)
8036 *aControl = mData->mSession.mDirectControl;
8037
8038#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8039 /* Additional session data */
8040 if (aIPCSem != NULL)
8041 *aIPCSem = aMachine->mIPCSem;
8042#endif
8043 return true;
8044 }
8045
8046 return false;
8047}
8048
8049/**
8050 * Returns @c true if the given machine has an spawning direct session and
8051 * returns and additional session data (on some platforms) if so.
8052 *
8053 * Note that when the method returns @c false, the arguments remain unchanged.
8054 *
8055 * @param aPID PID of the spawned direct session process.
8056 *
8057 * @note locks this object for reading.
8058 */
8059#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8060bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
8061#else
8062bool Machine::isSessionSpawning()
8063#endif
8064{
8065 AutoLimitedCaller autoCaller(this);
8066 AssertComRCReturn(autoCaller.rc(), false);
8067
8068 /* just return false for inaccessible machines */
8069 if (autoCaller.state() != Ready)
8070 return false;
8071
8072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8073
8074 if (mData->mSession.mState == SessionState_Spawning)
8075 {
8076#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8077 /* Additional session data */
8078 if (aPID != NULL)
8079 {
8080 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
8081 *aPID = mData->mSession.mPID;
8082 }
8083#endif
8084 return true;
8085 }
8086
8087 return false;
8088}
8089
8090/**
8091 * Called from the client watcher thread to check for unexpected client process
8092 * death during Session_Spawning state (e.g. before it successfully opened a
8093 * direct session).
8094 *
8095 * On Win32 and on OS/2, this method is called only when we've got the
8096 * direct client's process termination notification, so it always returns @c
8097 * true.
8098 *
8099 * On other platforms, this method returns @c true if the client process is
8100 * terminated and @c false if it's still alive.
8101 *
8102 * @note Locks this object for writing.
8103 */
8104bool Machine::checkForSpawnFailure()
8105{
8106 AutoCaller autoCaller(this);
8107 if (!autoCaller.isOk())
8108 {
8109 /* nothing to do */
8110 LogFlowThisFunc(("Already uninitialized!\n"));
8111 return true;
8112 }
8113
8114 /* VirtualBox::addProcessToReap() needs a write lock */
8115 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
8116
8117 if (mData->mSession.mState != SessionState_Spawning)
8118 {
8119 /* nothing to do */
8120 LogFlowThisFunc(("Not spawning any more!\n"));
8121 return true;
8122 }
8123
8124 HRESULT rc = S_OK;
8125
8126#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
8127
8128 /* the process was already unexpectedly terminated, we just need to set an
8129 * error and finalize session spawning */
8130 rc = setError(E_FAIL,
8131 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
8132 getName().c_str());
8133#else
8134
8135 /* PID not yet initialized, skip check. */
8136 if (mData->mSession.mPID == NIL_RTPROCESS)
8137 return false;
8138
8139 RTPROCSTATUS status;
8140 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
8141 &status);
8142
8143 if (vrc != VERR_PROCESS_RUNNING)
8144 {
8145 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8146 rc = setError(E_FAIL,
8147 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
8148 getName().c_str(), status.iStatus);
8149 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8150 rc = setError(E_FAIL,
8151 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
8152 getName().c_str(), status.iStatus);
8153 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8154 rc = setError(E_FAIL,
8155 tr("The virtual machine '%s' has terminated abnormally"),
8156 getName().c_str(), status.iStatus);
8157 else
8158 rc = setError(E_FAIL,
8159 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
8160 getName().c_str(), rc);
8161 }
8162
8163#endif
8164
8165 if (FAILED(rc))
8166 {
8167 /* Close the remote session, remove the remote control from the list
8168 * and reset session state to Closed (@note keep the code in sync with
8169 * the relevant part in checkForSpawnFailure()). */
8170
8171 Assert(mData->mSession.mRemoteControls.size() == 1);
8172 if (mData->mSession.mRemoteControls.size() == 1)
8173 {
8174 ErrorInfoKeeper eik;
8175 mData->mSession.mRemoteControls.front()->Uninitialize();
8176 }
8177
8178 mData->mSession.mRemoteControls.clear();
8179 mData->mSession.mState = SessionState_Unlocked;
8180
8181 /* finalize the progress after setting the state */
8182 if (!mData->mSession.mProgress.isNull())
8183 {
8184 mData->mSession.mProgress->notifyComplete(rc);
8185 mData->mSession.mProgress.setNull();
8186 }
8187
8188 mParent->addProcessToReap(mData->mSession.mPID);
8189 mData->mSession.mPID = NIL_RTPROCESS;
8190
8191 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8192 return true;
8193 }
8194
8195 return false;
8196}
8197
8198/**
8199 * Checks whether the machine can be registered. If so, commits and saves
8200 * all settings.
8201 *
8202 * @note Must be called from mParent's write lock. Locks this object and
8203 * children for writing.
8204 */
8205HRESULT Machine::prepareRegister()
8206{
8207 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8208
8209 AutoLimitedCaller autoCaller(this);
8210 AssertComRCReturnRC(autoCaller.rc());
8211
8212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8213
8214 /* wait for state dependents to drop to zero */
8215 ensureNoStateDependencies();
8216
8217 if (!mData->mAccessible)
8218 return setError(VBOX_E_INVALID_OBJECT_STATE,
8219 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8220 mUserData->s.strName.c_str(),
8221 mData->mUuid.toString().c_str());
8222
8223 AssertReturn(autoCaller.state() == Ready, E_FAIL);
8224
8225 if (mData->mRegistered)
8226 return setError(VBOX_E_INVALID_OBJECT_STATE,
8227 tr("The machine '%s' with UUID {%s} is already registered"),
8228 mUserData->s.strName.c_str(),
8229 mData->mUuid.toString().c_str());
8230
8231 HRESULT rc = S_OK;
8232
8233 // Ensure the settings are saved. If we are going to be registered and
8234 // no config file exists yet, create it by calling saveSettings() too.
8235 if ( (mData->flModifications)
8236 || (!mData->pMachineConfigFile->fileExists())
8237 )
8238 {
8239 rc = saveSettings(NULL);
8240 // no need to check whether VirtualBox.xml needs saving too since
8241 // we can't have a machine XML file rename pending
8242 if (FAILED(rc)) return rc;
8243 }
8244
8245 /* more config checking goes here */
8246
8247 if (SUCCEEDED(rc))
8248 {
8249 /* we may have had implicit modifications we want to fix on success */
8250 commit();
8251
8252 mData->mRegistered = true;
8253 }
8254 else
8255 {
8256 /* we may have had implicit modifications we want to cancel on failure*/
8257 rollback(false /* aNotify */);
8258 }
8259
8260 return rc;
8261}
8262
8263/**
8264 * Increases the number of objects dependent on the machine state or on the
8265 * registered state. Guarantees that these two states will not change at least
8266 * until #releaseStateDependency() is called.
8267 *
8268 * Depending on the @a aDepType value, additional state checks may be made.
8269 * These checks will set extended error info on failure. See
8270 * #checkStateDependency() for more info.
8271 *
8272 * If this method returns a failure, the dependency is not added and the caller
8273 * is not allowed to rely on any particular machine state or registration state
8274 * value and may return the failed result code to the upper level.
8275 *
8276 * @param aDepType Dependency type to add.
8277 * @param aState Current machine state (NULL if not interested).
8278 * @param aRegistered Current registered state (NULL if not interested).
8279 *
8280 * @note Locks this object for writing.
8281 */
8282HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8283 MachineState_T *aState /* = NULL */,
8284 BOOL *aRegistered /* = NULL */)
8285{
8286 AutoCaller autoCaller(this);
8287 AssertComRCReturnRC(autoCaller.rc());
8288
8289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8290
8291 HRESULT rc = checkStateDependency(aDepType);
8292 if (FAILED(rc)) return rc;
8293
8294 {
8295 if (mData->mMachineStateChangePending != 0)
8296 {
8297 /* ensureNoStateDependencies() is waiting for state dependencies to
8298 * drop to zero so don't add more. It may make sense to wait a bit
8299 * and retry before reporting an error (since the pending state
8300 * transition should be really quick) but let's just assert for
8301 * now to see if it ever happens on practice. */
8302
8303 AssertFailed();
8304
8305 return setError(E_ACCESSDENIED,
8306 tr("Machine state change is in progress. Please retry the operation later."));
8307 }
8308
8309 ++mData->mMachineStateDeps;
8310 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8311 }
8312
8313 if (aState)
8314 *aState = mData->mMachineState;
8315 if (aRegistered)
8316 *aRegistered = mData->mRegistered;
8317
8318 return S_OK;
8319}
8320
8321/**
8322 * Decreases the number of objects dependent on the machine state.
8323 * Must always complete the #addStateDependency() call after the state
8324 * dependency is no more necessary.
8325 */
8326void Machine::releaseStateDependency()
8327{
8328 AutoCaller autoCaller(this);
8329 AssertComRCReturnVoid(autoCaller.rc());
8330
8331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8332
8333 /* releaseStateDependency() w/o addStateDependency()? */
8334 AssertReturnVoid(mData->mMachineStateDeps != 0);
8335 -- mData->mMachineStateDeps;
8336
8337 if (mData->mMachineStateDeps == 0)
8338 {
8339 /* inform ensureNoStateDependencies() that there are no more deps */
8340 if (mData->mMachineStateChangePending != 0)
8341 {
8342 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8343 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8344 }
8345 }
8346}
8347
8348Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8349{
8350 /* start with nothing found */
8351 Utf8Str strResult("");
8352
8353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8354
8355 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8356 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8357 // found:
8358 strResult = it->second; // source is a Utf8Str
8359
8360 return strResult;
8361}
8362
8363// protected methods
8364/////////////////////////////////////////////////////////////////////////////
8365
8366/**
8367 * Performs machine state checks based on the @a aDepType value. If a check
8368 * fails, this method will set extended error info, otherwise it will return
8369 * S_OK. It is supposed, that on failure, the caller will immediately return
8370 * the return value of this method to the upper level.
8371 *
8372 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8373 *
8374 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8375 * current state of this machine object allows to change settings of the
8376 * machine (i.e. the machine is not registered, or registered but not running
8377 * and not saved). It is useful to call this method from Machine setters
8378 * before performing any change.
8379 *
8380 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8381 * as for MutableStateDep except that if the machine is saved, S_OK is also
8382 * returned. This is useful in setters which allow changing machine
8383 * properties when it is in the saved state.
8384 *
8385 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8386 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8387 * Aborted).
8388 *
8389 * @param aDepType Dependency type to check.
8390 *
8391 * @note Non Machine based classes should use #addStateDependency() and
8392 * #releaseStateDependency() methods or the smart AutoStateDependency
8393 * template.
8394 *
8395 * @note This method must be called from under this object's read or write
8396 * lock.
8397 */
8398HRESULT Machine::checkStateDependency(StateDependency aDepType)
8399{
8400 switch (aDepType)
8401 {
8402 case AnyStateDep:
8403 {
8404 break;
8405 }
8406 case MutableStateDep:
8407 {
8408 if ( mData->mRegistered
8409 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8410 || ( mData->mMachineState != MachineState_Paused
8411 && mData->mMachineState != MachineState_Running
8412 && mData->mMachineState != MachineState_Aborted
8413 && mData->mMachineState != MachineState_Teleported
8414 && mData->mMachineState != MachineState_PoweredOff
8415 )
8416 )
8417 )
8418 return setError(VBOX_E_INVALID_VM_STATE,
8419 tr("The machine is not mutable (state is %s)"),
8420 Global::stringifyMachineState(mData->mMachineState));
8421 break;
8422 }
8423 case MutableOrSavedStateDep:
8424 {
8425 if ( mData->mRegistered
8426 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8427 || ( mData->mMachineState != MachineState_Paused
8428 && mData->mMachineState != MachineState_Running
8429 && mData->mMachineState != MachineState_Aborted
8430 && mData->mMachineState != MachineState_Teleported
8431 && mData->mMachineState != MachineState_Saved
8432 && mData->mMachineState != MachineState_PoweredOff
8433 )
8434 )
8435 )
8436 return setError(VBOX_E_INVALID_VM_STATE,
8437 tr("The machine is not mutable (state is %s)"),
8438 Global::stringifyMachineState(mData->mMachineState));
8439 break;
8440 }
8441 case OfflineStateDep:
8442 {
8443 if ( mData->mRegistered
8444 && ( !isSessionMachine()
8445 || ( mData->mMachineState != MachineState_PoweredOff
8446 && mData->mMachineState != MachineState_Saved
8447 && mData->mMachineState != MachineState_Aborted
8448 && mData->mMachineState != MachineState_Teleported
8449 )
8450 )
8451 )
8452 return setError(VBOX_E_INVALID_VM_STATE,
8453 tr("The machine is not offline (state is %s)"),
8454 Global::stringifyMachineState(mData->mMachineState));
8455 break;
8456 }
8457 }
8458
8459 return S_OK;
8460}
8461
8462/**
8463 * Helper to initialize all associated child objects and allocate data
8464 * structures.
8465 *
8466 * This method must be called as a part of the object's initialization procedure
8467 * (usually done in the #init() method).
8468 *
8469 * @note Must be called only from #init() or from #registeredInit().
8470 */
8471HRESULT Machine::initDataAndChildObjects()
8472{
8473 AutoCaller autoCaller(this);
8474 AssertComRCReturnRC(autoCaller.rc());
8475 AssertComRCReturn(autoCaller.state() == InInit ||
8476 autoCaller.state() == Limited, E_FAIL);
8477
8478 AssertReturn(!mData->mAccessible, E_FAIL);
8479
8480 /* allocate data structures */
8481 mSSData.allocate();
8482 mUserData.allocate();
8483 mHWData.allocate();
8484 mMediaData.allocate();
8485 mStorageControllers.allocate();
8486
8487 /* initialize mOSTypeId */
8488 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8489
8490 /* create associated BIOS settings object */
8491 unconst(mBIOSSettings).createObject();
8492 mBIOSSettings->init(this);
8493
8494 /* create an associated VRDE object (default is disabled) */
8495 unconst(mVRDEServer).createObject();
8496 mVRDEServer->init(this);
8497
8498 /* create associated serial port objects */
8499 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8500 {
8501 unconst(mSerialPorts[slot]).createObject();
8502 mSerialPorts[slot]->init(this, slot);
8503 }
8504
8505 /* create associated parallel port objects */
8506 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8507 {
8508 unconst(mParallelPorts[slot]).createObject();
8509 mParallelPorts[slot]->init(this, slot);
8510 }
8511
8512 /* create the audio adapter object (always present, default is disabled) */
8513 unconst(mAudioAdapter).createObject();
8514 mAudioAdapter->init(this);
8515
8516 /* create the USB controller object (always present, default is disabled) */
8517 unconst(mUSBController).createObject();
8518 mUSBController->init(this);
8519
8520 /* create the USB device filters object (always present) */
8521 unconst(mUSBDeviceFilters).createObject();
8522 mUSBDeviceFilters->init(this);
8523
8524 /* create associated network adapter objects */
8525 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8527 {
8528 unconst(mNetworkAdapters[slot]).createObject();
8529 mNetworkAdapters[slot]->init(this, slot);
8530 }
8531
8532 /* create the bandwidth control */
8533 unconst(mBandwidthControl).createObject();
8534 mBandwidthControl->init(this);
8535
8536 return S_OK;
8537}
8538
8539/**
8540 * Helper to uninitialize all associated child objects and to free all data
8541 * structures.
8542 *
8543 * This method must be called as a part of the object's uninitialization
8544 * procedure (usually done in the #uninit() method).
8545 *
8546 * @note Must be called only from #uninit() or from #registeredInit().
8547 */
8548void Machine::uninitDataAndChildObjects()
8549{
8550 AutoCaller autoCaller(this);
8551 AssertComRCReturnVoid(autoCaller.rc());
8552 AssertComRCReturnVoid( autoCaller.state() == InUninit
8553 || autoCaller.state() == Limited);
8554
8555 /* tell all our other child objects we've been uninitialized */
8556 if (mBandwidthControl)
8557 {
8558 mBandwidthControl->uninit();
8559 unconst(mBandwidthControl).setNull();
8560 }
8561
8562 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8563 {
8564 if (mNetworkAdapters[slot])
8565 {
8566 mNetworkAdapters[slot]->uninit();
8567 unconst(mNetworkAdapters[slot]).setNull();
8568 }
8569 }
8570
8571 if (mUSBController)
8572 {
8573 mUSBController->uninit();
8574 unconst(mUSBController).setNull();
8575 }
8576
8577 if (mUSBDeviceFilters)
8578 {
8579 mUSBDeviceFilters->uninit();
8580 unconst(mUSBDeviceFilters).setNull();
8581 }
8582
8583 if (mAudioAdapter)
8584 {
8585 mAudioAdapter->uninit();
8586 unconst(mAudioAdapter).setNull();
8587 }
8588
8589 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8590 {
8591 if (mParallelPorts[slot])
8592 {
8593 mParallelPorts[slot]->uninit();
8594 unconst(mParallelPorts[slot]).setNull();
8595 }
8596 }
8597
8598 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8599 {
8600 if (mSerialPorts[slot])
8601 {
8602 mSerialPorts[slot]->uninit();
8603 unconst(mSerialPorts[slot]).setNull();
8604 }
8605 }
8606
8607 if (mVRDEServer)
8608 {
8609 mVRDEServer->uninit();
8610 unconst(mVRDEServer).setNull();
8611 }
8612
8613 if (mBIOSSettings)
8614 {
8615 mBIOSSettings->uninit();
8616 unconst(mBIOSSettings).setNull();
8617 }
8618
8619 /* Deassociate media (only when a real Machine or a SnapshotMachine
8620 * instance is uninitialized; SessionMachine instances refer to real
8621 * Machine media). This is necessary for a clean re-initialization of
8622 * the VM after successfully re-checking the accessibility state. Note
8623 * that in case of normal Machine or SnapshotMachine uninitialization (as
8624 * a result of unregistering or deleting the snapshot), outdated media
8625 * attachments will already be uninitialized and deleted, so this
8626 * code will not affect them. */
8627 if ( !!mMediaData
8628 && (!isSessionMachine())
8629 )
8630 {
8631 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8632 it != mMediaData->mAttachments.end();
8633 ++it)
8634 {
8635 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8636 if (pMedium.isNull())
8637 continue;
8638 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8639 AssertComRC(rc);
8640 }
8641 }
8642
8643 if (!isSessionMachine() && !isSnapshotMachine())
8644 {
8645 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8646 if (mData->mFirstSnapshot)
8647 {
8648 // snapshots tree is protected by machine write lock; strictly
8649 // this isn't necessary here since we're deleting the entire
8650 // machine, but otherwise we assert in Snapshot::uninit()
8651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8652 mData->mFirstSnapshot->uninit();
8653 mData->mFirstSnapshot.setNull();
8654 }
8655
8656 mData->mCurrentSnapshot.setNull();
8657 }
8658
8659 /* free data structures (the essential mData structure is not freed here
8660 * since it may be still in use) */
8661 mMediaData.free();
8662 mStorageControllers.free();
8663 mHWData.free();
8664 mUserData.free();
8665 mSSData.free();
8666}
8667
8668/**
8669 * Returns a pointer to the Machine object for this machine that acts like a
8670 * parent for complex machine data objects such as shared folders, etc.
8671 *
8672 * For primary Machine objects and for SnapshotMachine objects, returns this
8673 * object's pointer itself. For SessionMachine objects, returns the peer
8674 * (primary) machine pointer.
8675 */
8676Machine* Machine::getMachine()
8677{
8678 if (isSessionMachine())
8679 return (Machine*)mPeer;
8680 return this;
8681}
8682
8683/**
8684 * Makes sure that there are no machine state dependents. If necessary, waits
8685 * for the number of dependents to drop to zero.
8686 *
8687 * Make sure this method is called from under this object's write lock to
8688 * guarantee that no new dependents may be added when this method returns
8689 * control to the caller.
8690 *
8691 * @note Locks this object for writing. The lock will be released while waiting
8692 * (if necessary).
8693 *
8694 * @warning To be used only in methods that change the machine state!
8695 */
8696void Machine::ensureNoStateDependencies()
8697{
8698 AssertReturnVoid(isWriteLockOnCurrentThread());
8699
8700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8701
8702 /* Wait for all state dependents if necessary */
8703 if (mData->mMachineStateDeps != 0)
8704 {
8705 /* lazy semaphore creation */
8706 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8707 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8708
8709 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8710 mData->mMachineStateDeps));
8711
8712 ++mData->mMachineStateChangePending;
8713
8714 /* reset the semaphore before waiting, the last dependent will signal
8715 * it */
8716 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8717
8718 alock.release();
8719
8720 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8721
8722 alock.acquire();
8723
8724 -- mData->mMachineStateChangePending;
8725 }
8726}
8727
8728/**
8729 * Changes the machine state and informs callbacks.
8730 *
8731 * This method is not intended to fail so it either returns S_OK or asserts (and
8732 * returns a failure).
8733 *
8734 * @note Locks this object for writing.
8735 */
8736HRESULT Machine::setMachineState(MachineState_T aMachineState)
8737{
8738 LogFlowThisFuncEnter();
8739 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8740
8741 AutoCaller autoCaller(this);
8742 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8743
8744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8745
8746 /* wait for state dependents to drop to zero */
8747 ensureNoStateDependencies();
8748
8749 if (mData->mMachineState != aMachineState)
8750 {
8751 mData->mMachineState = aMachineState;
8752
8753 RTTimeNow(&mData->mLastStateChange);
8754
8755 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8756 }
8757
8758 LogFlowThisFuncLeave();
8759 return S_OK;
8760}
8761
8762/**
8763 * Searches for a shared folder with the given logical name
8764 * in the collection of shared folders.
8765 *
8766 * @param aName logical name of the shared folder
8767 * @param aSharedFolder where to return the found object
8768 * @param aSetError whether to set the error info if the folder is
8769 * not found
8770 * @return
8771 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8772 *
8773 * @note
8774 * must be called from under the object's lock!
8775 */
8776HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8777 ComObjPtr<SharedFolder> &aSharedFolder,
8778 bool aSetError /* = false */)
8779{
8780 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8781 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8782 it != mHWData->mSharedFolders.end();
8783 ++it)
8784 {
8785 SharedFolder *pSF = *it;
8786 AutoCaller autoCaller(pSF);
8787 if (pSF->getName() == aName)
8788 {
8789 aSharedFolder = pSF;
8790 rc = S_OK;
8791 break;
8792 }
8793 }
8794
8795 if (aSetError && FAILED(rc))
8796 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8797
8798 return rc;
8799}
8800
8801/**
8802 * Initializes all machine instance data from the given settings structures
8803 * from XML. The exception is the machine UUID which needs special handling
8804 * depending on the caller's use case, so the caller needs to set that herself.
8805 *
8806 * This gets called in several contexts during machine initialization:
8807 *
8808 * -- When machine XML exists on disk already and needs to be loaded into memory,
8809 * for example, from registeredInit() to load all registered machines on
8810 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8811 * attached to the machine should be part of some media registry already.
8812 *
8813 * -- During OVF import, when a machine config has been constructed from an
8814 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8815 * ensure that the media listed as attachments in the config (which have
8816 * been imported from the OVF) receive the correct registry ID.
8817 *
8818 * -- During VM cloning.
8819 *
8820 * @param config Machine settings from XML.
8821 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8822 * @return
8823 */
8824HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8825 const Guid *puuidRegistry)
8826{
8827 // copy name, description, OS type, teleporter, UTC etc.
8828 #define DECODE_STR_MAX _1M
8829 mUserData->s = config.machineUserData;
8830
8831 // Decode the Icon overide data from config userdata and set onto Machine.
8832 const char* pszStr = config.machineUserData.ovIcon.c_str();
8833 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8834 if (cbOut > DECODE_STR_MAX)
8835 return setError(E_FAIL,
8836 tr("Icon Data too long.'%d' > '%d'"),
8837 cbOut,
8838 DECODE_STR_MAX);
8839 com::SafeArray<BYTE> iconByte(cbOut);
8840 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL);
8841 if (FAILED(rc))
8842 return setError(E_FAIL,
8843 tr("Failure to Decode Icon Data. '%s' (%d)"),
8844 pszStr,
8845 rc);
8846 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte));
8847
8848 // look up the object by Id to check it is valid
8849 ComPtr<IGuestOSType> guestOSType;
8850 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8851 guestOSType.asOutParam());
8852 if (FAILED(rc)) return rc;
8853
8854 // stateFile (optional)
8855 if (config.strStateFile.isEmpty())
8856 mSSData->strStateFilePath.setNull();
8857 else
8858 {
8859 Utf8Str stateFilePathFull(config.strStateFile);
8860 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8861 if (RT_FAILURE(vrc))
8862 return setError(E_FAIL,
8863 tr("Invalid saved state file path '%s' (%Rrc)"),
8864 config.strStateFile.c_str(),
8865 vrc);
8866 mSSData->strStateFilePath = stateFilePathFull;
8867 }
8868
8869 // snapshot folder needs special processing so set it again
8870 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8871 if (FAILED(rc)) return rc;
8872
8873 /* Copy the extra data items (Not in any case config is already the same as
8874 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8875 * make sure the extra data map is copied). */
8876 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8877
8878 /* currentStateModified (optional, default is true) */
8879 mData->mCurrentStateModified = config.fCurrentStateModified;
8880
8881 mData->mLastStateChange = config.timeLastStateChange;
8882
8883 /*
8884 * note: all mUserData members must be assigned prior this point because
8885 * we need to commit changes in order to let mUserData be shared by all
8886 * snapshot machine instances.
8887 */
8888 mUserData.commitCopy();
8889
8890 // machine registry, if present (must be loaded before snapshots)
8891 if (config.canHaveOwnMediaRegistry())
8892 {
8893 // determine machine folder
8894 Utf8Str strMachineFolder = getSettingsFileFull();
8895 strMachineFolder.stripFilename();
8896 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8897 config.mediaRegistry,
8898 strMachineFolder);
8899 if (FAILED(rc)) return rc;
8900 }
8901
8902 /* Snapshot node (optional) */
8903 size_t cRootSnapshots;
8904 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8905 {
8906 // there must be only one root snapshot
8907 Assert(cRootSnapshots == 1);
8908
8909 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8910
8911 rc = loadSnapshot(snap,
8912 config.uuidCurrentSnapshot,
8913 NULL); // no parent == first snapshot
8914 if (FAILED(rc)) return rc;
8915 }
8916
8917 // hardware data
8918 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8919 if (FAILED(rc)) return rc;
8920
8921 // load storage controllers
8922 rc = loadStorageControllers(config.storageMachine,
8923 puuidRegistry,
8924 NULL /* puuidSnapshot */);
8925 if (FAILED(rc)) return rc;
8926
8927 /*
8928 * NOTE: the assignment below must be the last thing to do,
8929 * otherwise it will be not possible to change the settings
8930 * somewhere in the code above because all setters will be
8931 * blocked by checkStateDependency(MutableStateDep).
8932 */
8933
8934 /* set the machine state to Aborted or Saved when appropriate */
8935 if (config.fAborted)
8936 {
8937 mSSData->strStateFilePath.setNull();
8938
8939 /* no need to use setMachineState() during init() */
8940 mData->mMachineState = MachineState_Aborted;
8941 }
8942 else if (!mSSData->strStateFilePath.isEmpty())
8943 {
8944 /* no need to use setMachineState() during init() */
8945 mData->mMachineState = MachineState_Saved;
8946 }
8947
8948 // after loading settings, we are no longer different from the XML on disk
8949 mData->flModifications = 0;
8950
8951 return S_OK;
8952}
8953
8954/**
8955 * Recursively loads all snapshots starting from the given.
8956 *
8957 * @param aNode <Snapshot> node.
8958 * @param aCurSnapshotId Current snapshot ID from the settings file.
8959 * @param aParentSnapshot Parent snapshot.
8960 */
8961HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8962 const Guid &aCurSnapshotId,
8963 Snapshot *aParentSnapshot)
8964{
8965 AssertReturn(!isSnapshotMachine(), E_FAIL);
8966 AssertReturn(!isSessionMachine(), E_FAIL);
8967
8968 HRESULT rc = S_OK;
8969
8970 Utf8Str strStateFile;
8971 if (!data.strStateFile.isEmpty())
8972 {
8973 /* optional */
8974 strStateFile = data.strStateFile;
8975 int vrc = calculateFullPath(strStateFile, strStateFile);
8976 if (RT_FAILURE(vrc))
8977 return setError(E_FAIL,
8978 tr("Invalid saved state file path '%s' (%Rrc)"),
8979 strStateFile.c_str(),
8980 vrc);
8981 }
8982
8983 /* create a snapshot machine object */
8984 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8985 pSnapshotMachine.createObject();
8986 rc = pSnapshotMachine->initFromSettings(this,
8987 data.hardware,
8988 &data.debugging,
8989 &data.autostart,
8990 data.storage,
8991 data.uuid.ref(),
8992 strStateFile);
8993 if (FAILED(rc)) return rc;
8994
8995 /* create a snapshot object */
8996 ComObjPtr<Snapshot> pSnapshot;
8997 pSnapshot.createObject();
8998 /* initialize the snapshot */
8999 rc = pSnapshot->init(mParent, // VirtualBox object
9000 data.uuid,
9001 data.strName,
9002 data.strDescription,
9003 data.timestamp,
9004 pSnapshotMachine,
9005 aParentSnapshot);
9006 if (FAILED(rc)) return rc;
9007
9008 /* memorize the first snapshot if necessary */
9009 if (!mData->mFirstSnapshot)
9010 mData->mFirstSnapshot = pSnapshot;
9011
9012 /* memorize the current snapshot when appropriate */
9013 if ( !mData->mCurrentSnapshot
9014 && pSnapshot->getId() == aCurSnapshotId
9015 )
9016 mData->mCurrentSnapshot = pSnapshot;
9017
9018 // now create the children
9019 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
9020 it != data.llChildSnapshots.end();
9021 ++it)
9022 {
9023 const settings::Snapshot &childData = *it;
9024 // recurse
9025 rc = loadSnapshot(childData,
9026 aCurSnapshotId,
9027 pSnapshot); // parent = the one we created above
9028 if (FAILED(rc)) return rc;
9029 }
9030
9031 return rc;
9032}
9033
9034/**
9035 * Loads settings into mHWData.
9036 *
9037 * @param data Reference to the hardware settings.
9038 * @param pDbg Pointer to the debugging settings.
9039 * @param pAutostart Pointer to the autostart settings.
9040 */
9041HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
9042 const settings::Autostart *pAutostart)
9043{
9044 AssertReturn(!isSessionMachine(), E_FAIL);
9045
9046 HRESULT rc = S_OK;
9047
9048 try
9049 {
9050 /* The hardware version attribute (optional). */
9051 mHWData->mHWVersion = data.strVersion;
9052 mHWData->mHardwareUUID = data.uuid;
9053
9054 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9055 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
9056 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9057 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9058 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9059 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9060 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9061 mHWData->mPAEEnabled = data.fPAE;
9062 mHWData->mSyntheticCpu = data.fSyntheticCpu;
9063 mHWData->mLongMode = data.enmLongMode;
9064 mHWData->mCPUCount = data.cCPUs;
9065 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9066 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9067
9068 // cpu
9069 if (mHWData->mCPUHotPlugEnabled)
9070 {
9071 for (settings::CpuList::const_iterator it = data.llCpus.begin();
9072 it != data.llCpus.end();
9073 ++it)
9074 {
9075 const settings::Cpu &cpu = *it;
9076
9077 mHWData->mCPUAttached[cpu.ulId] = true;
9078 }
9079 }
9080
9081 // cpuid leafs
9082 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
9083 it != data.llCpuIdLeafs.end();
9084 ++it)
9085 {
9086 const settings::CpuIdLeaf &leaf = *it;
9087
9088 switch (leaf.ulId)
9089 {
9090 case 0x0:
9091 case 0x1:
9092 case 0x2:
9093 case 0x3:
9094 case 0x4:
9095 case 0x5:
9096 case 0x6:
9097 case 0x7:
9098 case 0x8:
9099 case 0x9:
9100 case 0xA:
9101 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9102 break;
9103
9104 case 0x80000000:
9105 case 0x80000001:
9106 case 0x80000002:
9107 case 0x80000003:
9108 case 0x80000004:
9109 case 0x80000005:
9110 case 0x80000006:
9111 case 0x80000007:
9112 case 0x80000008:
9113 case 0x80000009:
9114 case 0x8000000A:
9115 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9116 break;
9117
9118 default:
9119 /* just ignore */
9120 break;
9121 }
9122 }
9123
9124 mHWData->mMemorySize = data.ulMemorySizeMB;
9125 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9126
9127 // boot order
9128 for (size_t i = 0;
9129 i < RT_ELEMENTS(mHWData->mBootOrder);
9130 i++)
9131 {
9132 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9133 if (it == data.mapBootOrder.end())
9134 mHWData->mBootOrder[i] = DeviceType_Null;
9135 else
9136 mHWData->mBootOrder[i] = it->second;
9137 }
9138
9139 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9140 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9141 mHWData->mMonitorCount = data.cMonitors;
9142 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9143 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9144 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9145 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9146 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9147 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++)
9148 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9149 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9150 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9151 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9152 if (!data.strVideoCaptureFile.isEmpty())
9153 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9154 else
9155 mHWData->mVideoCaptureFile.setNull();
9156 mHWData->mFirmwareType = data.firmwareType;
9157 mHWData->mPointingHIDType = data.pointingHIDType;
9158 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9159 mHWData->mChipsetType = data.chipsetType;
9160 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
9161 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9162 mHWData->mHPETEnabled = data.fHPETEnabled;
9163
9164 /* VRDEServer */
9165 rc = mVRDEServer->loadSettings(data.vrdeSettings);
9166 if (FAILED(rc)) return rc;
9167
9168 /* BIOS */
9169 rc = mBIOSSettings->loadSettings(data.biosSettings);
9170 if (FAILED(rc)) return rc;
9171
9172 // Bandwidth control (must come before network adapters)
9173 rc = mBandwidthControl->loadSettings(data.ioSettings);
9174 if (FAILED(rc)) return rc;
9175
9176 /* USB Controller */
9177 rc = mUSBController->loadSettings(data.usbController);
9178 if (FAILED(rc)) return rc;
9179
9180 /* USB device filters */
9181 rc = mUSBDeviceFilters->loadSettings(data.usbController);
9182 if (FAILED(rc)) return rc;
9183
9184 // network adapters
9185 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9186 uint32_t oldCount = mNetworkAdapters.size();
9187 if (newCount > oldCount)
9188 {
9189 mNetworkAdapters.resize(newCount);
9190 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
9191 {
9192 unconst(mNetworkAdapters[slot]).createObject();
9193 mNetworkAdapters[slot]->init(this, slot);
9194 }
9195 }
9196 else if (newCount < oldCount)
9197 mNetworkAdapters.resize(newCount);
9198 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
9199 it != data.llNetworkAdapters.end();
9200 ++it)
9201 {
9202 const settings::NetworkAdapter &nic = *it;
9203
9204 /* slot unicity is guaranteed by XML Schema */
9205 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9206 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
9207 if (FAILED(rc)) return rc;
9208 }
9209
9210 // serial ports
9211 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
9212 it != data.llSerialPorts.end();
9213 ++it)
9214 {
9215 const settings::SerialPort &s = *it;
9216
9217 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9218 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
9219 if (FAILED(rc)) return rc;
9220 }
9221
9222 // parallel ports (optional)
9223 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
9224 it != data.llParallelPorts.end();
9225 ++it)
9226 {
9227 const settings::ParallelPort &p = *it;
9228
9229 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9230 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
9231 if (FAILED(rc)) return rc;
9232 }
9233
9234 /* AudioAdapter */
9235 rc = mAudioAdapter->loadSettings(data.audioAdapter);
9236 if (FAILED(rc)) return rc;
9237
9238 /* Shared folders */
9239 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
9240 it != data.llSharedFolders.end();
9241 ++it)
9242 {
9243 const settings::SharedFolder &sf = *it;
9244
9245 ComObjPtr<SharedFolder> sharedFolder;
9246 /* Check for double entries. Not allowed! */
9247 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9248 if (SUCCEEDED(rc))
9249 return setError(VBOX_E_OBJECT_IN_USE,
9250 tr("Shared folder named '%s' already exists"),
9251 sf.strName.c_str());
9252
9253 /* Create the new shared folder. Don't break on error. This will be
9254 * reported when the machine starts. */
9255 sharedFolder.createObject();
9256 rc = sharedFolder->init(getMachine(),
9257 sf.strName,
9258 sf.strHostPath,
9259 RT_BOOL(sf.fWritable),
9260 RT_BOOL(sf.fAutoMount),
9261 false /* fFailOnError */);
9262 if (FAILED(rc)) return rc;
9263 mHWData->mSharedFolders.push_back(sharedFolder);
9264 }
9265
9266 // Clipboard
9267 mHWData->mClipboardMode = data.clipboardMode;
9268
9269 // drag'n'drop
9270 mHWData->mDragAndDropMode = data.dragAndDropMode;
9271
9272 // guest settings
9273 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9274
9275 // IO settings
9276 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9277 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9278
9279 // Host PCI devices
9280 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9281 it != data.pciAttachments.end();
9282 ++it)
9283 {
9284 const settings::HostPCIDeviceAttachment &hpda = *it;
9285 ComObjPtr<PCIDeviceAttachment> pda;
9286
9287 pda.createObject();
9288 pda->loadSettings(this, hpda);
9289 mHWData->mPCIDeviceAssignments.push_back(pda);
9290 }
9291
9292 /*
9293 * (The following isn't really real hardware, but it lives in HWData
9294 * for reasons of convenience.)
9295 */
9296
9297#ifdef VBOX_WITH_GUEST_PROPS
9298 /* Guest properties (optional) */
9299 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
9300 it != data.llGuestProperties.end();
9301 ++it)
9302 {
9303 const settings::GuestProperty &prop = *it;
9304 uint32_t fFlags = guestProp::NILFLAG;
9305 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9306 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9307 mHWData->mGuestProperties[prop.strName] = property;
9308 }
9309
9310 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
9311#endif /* VBOX_WITH_GUEST_PROPS defined */
9312
9313 rc = loadDebugging(pDbg);
9314 if (FAILED(rc))
9315 return rc;
9316
9317 mHWData->mAutostart = *pAutostart;
9318
9319 /* default frontend */
9320 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9321 }
9322 catch(std::bad_alloc &)
9323 {
9324 return E_OUTOFMEMORY;
9325 }
9326
9327 AssertComRC(rc);
9328 return rc;
9329}
9330
9331/**
9332 * Called from Machine::loadHardware() to load the debugging settings of the
9333 * machine.
9334 *
9335 * @param pDbg Pointer to the settings.
9336 */
9337HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
9338{
9339 mHWData->mDebugging = *pDbg;
9340 /* no more processing currently required, this will probably change. */
9341 return S_OK;
9342}
9343
9344/**
9345 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
9346 *
9347 * @param data
9348 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9349 * @param puuidSnapshot
9350 * @return
9351 */
9352HRESULT Machine::loadStorageControllers(const settings::Storage &data,
9353 const Guid *puuidRegistry,
9354 const Guid *puuidSnapshot)
9355{
9356 AssertReturn(!isSessionMachine(), E_FAIL);
9357
9358 HRESULT rc = S_OK;
9359
9360 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9361 it != data.llStorageControllers.end();
9362 ++it)
9363 {
9364 const settings::StorageController &ctlData = *it;
9365
9366 ComObjPtr<StorageController> pCtl;
9367 /* Try to find one with the name first. */
9368 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9369 if (SUCCEEDED(rc))
9370 return setError(VBOX_E_OBJECT_IN_USE,
9371 tr("Storage controller named '%s' already exists"),
9372 ctlData.strName.c_str());
9373
9374 pCtl.createObject();
9375 rc = pCtl->init(this,
9376 ctlData.strName,
9377 ctlData.storageBus,
9378 ctlData.ulInstance,
9379 ctlData.fBootable);
9380 if (FAILED(rc)) return rc;
9381
9382 mStorageControllers->push_back(pCtl);
9383
9384 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9385 if (FAILED(rc)) return rc;
9386
9387 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9388 if (FAILED(rc)) return rc;
9389
9390 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9391 if (FAILED(rc)) return rc;
9392
9393 /* Set IDE emulation settings (only for AHCI controller). */
9394 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9395 {
9396 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9397 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9398 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9399 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9400 )
9401 return rc;
9402 }
9403
9404 /* Load the attached devices now. */
9405 rc = loadStorageDevices(pCtl,
9406 ctlData,
9407 puuidRegistry,
9408 puuidSnapshot);
9409 if (FAILED(rc)) return rc;
9410 }
9411
9412 return S_OK;
9413}
9414
9415/**
9416 * Called from loadStorageControllers for a controller's devices.
9417 *
9418 * @param aStorageController
9419 * @param data
9420 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9421 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9422 * @return
9423 */
9424HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9425 const settings::StorageController &data,
9426 const Guid *puuidRegistry,
9427 const Guid *puuidSnapshot)
9428{
9429 HRESULT rc = S_OK;
9430
9431 /* paranoia: detect duplicate attachments */
9432 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9433 it != data.llAttachedDevices.end();
9434 ++it)
9435 {
9436 const settings::AttachedDevice &ad = *it;
9437
9438 for (settings::AttachedDevicesList::const_iterator it2 = it;
9439 it2 != data.llAttachedDevices.end();
9440 ++it2)
9441 {
9442 if (it == it2)
9443 continue;
9444
9445 const settings::AttachedDevice &ad2 = *it2;
9446
9447 if ( ad.lPort == ad2.lPort
9448 && ad.lDevice == ad2.lDevice)
9449 {
9450 return setError(E_FAIL,
9451 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9452 aStorageController->getName().c_str(),
9453 ad.lPort,
9454 ad.lDevice,
9455 mUserData->s.strName.c_str());
9456 }
9457 }
9458 }
9459
9460 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9461 it != data.llAttachedDevices.end();
9462 ++it)
9463 {
9464 const settings::AttachedDevice &dev = *it;
9465 ComObjPtr<Medium> medium;
9466
9467 switch (dev.deviceType)
9468 {
9469 case DeviceType_Floppy:
9470 case DeviceType_DVD:
9471 if (dev.strHostDriveSrc.isNotEmpty())
9472 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9473 else
9474 rc = mParent->findRemoveableMedium(dev.deviceType,
9475 dev.uuid,
9476 false /* fRefresh */,
9477 false /* aSetError */,
9478 medium);
9479 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9480 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9481 rc = S_OK;
9482 break;
9483
9484 case DeviceType_HardDisk:
9485 {
9486 /* find a hard disk by UUID */
9487 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9488 if (FAILED(rc))
9489 {
9490 if (isSnapshotMachine())
9491 {
9492 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9493 // so the user knows that the bad disk is in a snapshot somewhere
9494 com::ErrorInfo info;
9495 return setError(E_FAIL,
9496 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9497 puuidSnapshot->raw(),
9498 info.getText().raw());
9499 }
9500 else
9501 return rc;
9502 }
9503
9504 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9505
9506 if (medium->getType() == MediumType_Immutable)
9507 {
9508 if (isSnapshotMachine())
9509 return setError(E_FAIL,
9510 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9511 "of the virtual machine '%s' ('%s')"),
9512 medium->getLocationFull().c_str(),
9513 dev.uuid.raw(),
9514 puuidSnapshot->raw(),
9515 mUserData->s.strName.c_str(),
9516 mData->m_strConfigFileFull.c_str());
9517
9518 return setError(E_FAIL,
9519 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9520 medium->getLocationFull().c_str(),
9521 dev.uuid.raw(),
9522 mUserData->s.strName.c_str(),
9523 mData->m_strConfigFileFull.c_str());
9524 }
9525
9526 if (medium->getType() == MediumType_MultiAttach)
9527 {
9528 if (isSnapshotMachine())
9529 return setError(E_FAIL,
9530 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9531 "of the virtual machine '%s' ('%s')"),
9532 medium->getLocationFull().c_str(),
9533 dev.uuid.raw(),
9534 puuidSnapshot->raw(),
9535 mUserData->s.strName.c_str(),
9536 mData->m_strConfigFileFull.c_str());
9537
9538 return setError(E_FAIL,
9539 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9540 medium->getLocationFull().c_str(),
9541 dev.uuid.raw(),
9542 mUserData->s.strName.c_str(),
9543 mData->m_strConfigFileFull.c_str());
9544 }
9545
9546 if ( !isSnapshotMachine()
9547 && medium->getChildren().size() != 0
9548 )
9549 return setError(E_FAIL,
9550 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9551 "because it has %d differencing child hard disks"),
9552 medium->getLocationFull().c_str(),
9553 dev.uuid.raw(),
9554 mUserData->s.strName.c_str(),
9555 mData->m_strConfigFileFull.c_str(),
9556 medium->getChildren().size());
9557
9558 if (findAttachment(mMediaData->mAttachments,
9559 medium))
9560 return setError(E_FAIL,
9561 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9562 medium->getLocationFull().c_str(),
9563 dev.uuid.raw(),
9564 mUserData->s.strName.c_str(),
9565 mData->m_strConfigFileFull.c_str());
9566
9567 break;
9568 }
9569
9570 default:
9571 return setError(E_FAIL,
9572 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9573 medium->getLocationFull().c_str(),
9574 mUserData->s.strName.c_str(),
9575 mData->m_strConfigFileFull.c_str());
9576 }
9577
9578 if (FAILED(rc))
9579 break;
9580
9581 /* Bandwidth groups are loaded at this point. */
9582 ComObjPtr<BandwidthGroup> pBwGroup;
9583
9584 if (!dev.strBwGroup.isEmpty())
9585 {
9586 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9587 if (FAILED(rc))
9588 return setError(E_FAIL,
9589 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9590 medium->getLocationFull().c_str(),
9591 dev.strBwGroup.c_str(),
9592 mUserData->s.strName.c_str(),
9593 mData->m_strConfigFileFull.c_str());
9594 pBwGroup->reference();
9595 }
9596
9597 const Bstr controllerName = aStorageController->getName();
9598 ComObjPtr<MediumAttachment> pAttachment;
9599 pAttachment.createObject();
9600 rc = pAttachment->init(this,
9601 medium,
9602 controllerName,
9603 dev.lPort,
9604 dev.lDevice,
9605 dev.deviceType,
9606 false,
9607 dev.fPassThrough,
9608 dev.fTempEject,
9609 dev.fNonRotational,
9610 dev.fDiscard,
9611 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9612 if (FAILED(rc)) break;
9613
9614 /* associate the medium with this machine and snapshot */
9615 if (!medium.isNull())
9616 {
9617 AutoCaller medCaller(medium);
9618 if (FAILED(medCaller.rc())) return medCaller.rc();
9619 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9620
9621 if (isSnapshotMachine())
9622 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9623 else
9624 rc = medium->addBackReference(mData->mUuid);
9625 /* If the medium->addBackReference fails it sets an appropriate
9626 * error message, so no need to do any guesswork here. */
9627
9628 if (puuidRegistry)
9629 // caller wants registry ID to be set on all attached media (OVF import case)
9630 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9631 }
9632
9633 if (FAILED(rc))
9634 break;
9635
9636 /* back up mMediaData to let registeredInit() properly rollback on failure
9637 * (= limited accessibility) */
9638 setModified(IsModified_Storage);
9639 mMediaData.backup();
9640 mMediaData->mAttachments.push_back(pAttachment);
9641 }
9642
9643 return rc;
9644}
9645
9646/**
9647 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9648 *
9649 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9650 * @param aSnapshot where to return the found snapshot
9651 * @param aSetError true to set extended error info on failure
9652 */
9653HRESULT Machine::findSnapshotById(const Guid &aId,
9654 ComObjPtr<Snapshot> &aSnapshot,
9655 bool aSetError /* = false */)
9656{
9657 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9658
9659 if (!mData->mFirstSnapshot)
9660 {
9661 if (aSetError)
9662 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9663 return E_FAIL;
9664 }
9665
9666 if (aId.isZero())
9667 aSnapshot = mData->mFirstSnapshot;
9668 else
9669 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9670
9671 if (!aSnapshot)
9672 {
9673 if (aSetError)
9674 return setError(E_FAIL,
9675 tr("Could not find a snapshot with UUID {%s}"),
9676 aId.toString().c_str());
9677 return E_FAIL;
9678 }
9679
9680 return S_OK;
9681}
9682
9683/**
9684 * Returns the snapshot with the given name or fails of no such snapshot.
9685 *
9686 * @param aName snapshot name to find
9687 * @param aSnapshot where to return the found snapshot
9688 * @param aSetError true to set extended error info on failure
9689 */
9690HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9691 ComObjPtr<Snapshot> &aSnapshot,
9692 bool aSetError /* = false */)
9693{
9694 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9695
9696 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9697
9698 if (!mData->mFirstSnapshot)
9699 {
9700 if (aSetError)
9701 return setError(VBOX_E_OBJECT_NOT_FOUND,
9702 tr("This machine does not have any snapshots"));
9703 return VBOX_E_OBJECT_NOT_FOUND;
9704 }
9705
9706 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9707
9708 if (!aSnapshot)
9709 {
9710 if (aSetError)
9711 return setError(VBOX_E_OBJECT_NOT_FOUND,
9712 tr("Could not find a snapshot named '%s'"), strName.c_str());
9713 return VBOX_E_OBJECT_NOT_FOUND;
9714 }
9715
9716 return S_OK;
9717}
9718
9719/**
9720 * Returns a storage controller object with the given name.
9721 *
9722 * @param aName storage controller name to find
9723 * @param aStorageController where to return the found storage controller
9724 * @param aSetError true to set extended error info on failure
9725 */
9726HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9727 ComObjPtr<StorageController> &aStorageController,
9728 bool aSetError /* = false */)
9729{
9730 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9731
9732 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9733 it != mStorageControllers->end();
9734 ++it)
9735 {
9736 if ((*it)->getName() == aName)
9737 {
9738 aStorageController = (*it);
9739 return S_OK;
9740 }
9741 }
9742
9743 if (aSetError)
9744 return setError(VBOX_E_OBJECT_NOT_FOUND,
9745 tr("Could not find a storage controller named '%s'"),
9746 aName.c_str());
9747 return VBOX_E_OBJECT_NOT_FOUND;
9748}
9749
9750HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9751 MediaData::AttachmentList &atts)
9752{
9753 AutoCaller autoCaller(this);
9754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9755
9756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9757
9758 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9759 it != mMediaData->mAttachments.end();
9760 ++it)
9761 {
9762 const ComObjPtr<MediumAttachment> &pAtt = *it;
9763
9764 // should never happen, but deal with NULL pointers in the list.
9765 AssertStmt(!pAtt.isNull(), continue);
9766
9767 // getControllerName() needs caller+read lock
9768 AutoCaller autoAttCaller(pAtt);
9769 if (FAILED(autoAttCaller.rc()))
9770 {
9771 atts.clear();
9772 return autoAttCaller.rc();
9773 }
9774 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9775
9776 if (pAtt->getControllerName() == aName)
9777 atts.push_back(pAtt);
9778 }
9779
9780 return S_OK;
9781}
9782
9783/**
9784 * Helper for #saveSettings. Cares about renaming the settings directory and
9785 * file if the machine name was changed and about creating a new settings file
9786 * if this is a new machine.
9787 *
9788 * @note Must be never called directly but only from #saveSettings().
9789 */
9790HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9791{
9792 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9793
9794 HRESULT rc = S_OK;
9795
9796 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9797
9798 /// @todo need to handle primary group change, too
9799
9800 /* attempt to rename the settings file if machine name is changed */
9801 if ( mUserData->s.fNameSync
9802 && mUserData.isBackedUp()
9803 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9804 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9805 )
9806 {
9807 bool dirRenamed = false;
9808 bool fileRenamed = false;
9809
9810 Utf8Str configFile, newConfigFile;
9811 Utf8Str configFilePrev, newConfigFilePrev;
9812 Utf8Str configDir, newConfigDir;
9813
9814 do
9815 {
9816 int vrc = VINF_SUCCESS;
9817
9818 Utf8Str name = mUserData.backedUpData()->s.strName;
9819 Utf8Str newName = mUserData->s.strName;
9820 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9821 if (group == "/")
9822 group.setNull();
9823 Utf8Str newGroup = mUserData->s.llGroups.front();
9824 if (newGroup == "/")
9825 newGroup.setNull();
9826
9827 configFile = mData->m_strConfigFileFull;
9828
9829 /* first, rename the directory if it matches the group and machine name */
9830 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9831 group.c_str(), RTPATH_DELIMITER, name.c_str());
9832 /** @todo hack, make somehow use of ComposeMachineFilename */
9833 if (mUserData->s.fDirectoryIncludesUUID)
9834 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9835 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9836 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9837 /** @todo hack, make somehow use of ComposeMachineFilename */
9838 if (mUserData->s.fDirectoryIncludesUUID)
9839 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9840 configDir = configFile;
9841 configDir.stripFilename();
9842 newConfigDir = configDir;
9843 if ( configDir.length() >= groupPlusName.length()
9844 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9845 {
9846 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9847 Utf8Str newConfigBaseDir(newConfigDir);
9848 newConfigDir.append(newGroupPlusName);
9849 /* consistency: use \ if appropriate on the platform */
9850 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9851 /* new dir and old dir cannot be equal here because of 'if'
9852 * above and because name != newName */
9853 Assert(configDir != newConfigDir);
9854 if (!fSettingsFileIsNew)
9855 {
9856 /* perform real rename only if the machine is not new */
9857 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9858 if ( vrc == VERR_FILE_NOT_FOUND
9859 || vrc == VERR_PATH_NOT_FOUND)
9860 {
9861 /* create the parent directory, then retry renaming */
9862 Utf8Str parent(newConfigDir);
9863 parent.stripFilename();
9864 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9865 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9866 }
9867 if (RT_FAILURE(vrc))
9868 {
9869 rc = setError(E_FAIL,
9870 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9871 configDir.c_str(),
9872 newConfigDir.c_str(),
9873 vrc);
9874 break;
9875 }
9876 /* delete subdirectories which are no longer needed */
9877 Utf8Str dir(configDir);
9878 dir.stripFilename();
9879 while (dir != newConfigBaseDir && dir != ".")
9880 {
9881 vrc = RTDirRemove(dir.c_str());
9882 if (RT_FAILURE(vrc))
9883 break;
9884 dir.stripFilename();
9885 }
9886 dirRenamed = true;
9887 }
9888 }
9889
9890 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9891 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9892
9893 /* then try to rename the settings file itself */
9894 if (newConfigFile != configFile)
9895 {
9896 /* get the path to old settings file in renamed directory */
9897 configFile = Utf8StrFmt("%s%c%s",
9898 newConfigDir.c_str(),
9899 RTPATH_DELIMITER,
9900 RTPathFilename(configFile.c_str()));
9901 if (!fSettingsFileIsNew)
9902 {
9903 /* perform real rename only if the machine is not new */
9904 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9905 if (RT_FAILURE(vrc))
9906 {
9907 rc = setError(E_FAIL,
9908 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9909 configFile.c_str(),
9910 newConfigFile.c_str(),
9911 vrc);
9912 break;
9913 }
9914 fileRenamed = true;
9915 configFilePrev = configFile;
9916 configFilePrev += "-prev";
9917 newConfigFilePrev = newConfigFile;
9918 newConfigFilePrev += "-prev";
9919 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9920 }
9921 }
9922
9923 // update m_strConfigFileFull amd mConfigFile
9924 mData->m_strConfigFileFull = newConfigFile;
9925 // compute the relative path too
9926 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9927
9928 // store the old and new so that VirtualBox::saveSettings() can update
9929 // the media registry
9930 if ( mData->mRegistered
9931 && configDir != newConfigDir)
9932 {
9933 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9934
9935 if (pfNeedsGlobalSaveSettings)
9936 *pfNeedsGlobalSaveSettings = true;
9937 }
9938
9939 // in the saved state file path, replace the old directory with the new directory
9940 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9941 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9942
9943 // and do the same thing for the saved state file paths of all the online snapshots
9944 if (mData->mFirstSnapshot)
9945 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9946 newConfigDir.c_str());
9947 }
9948 while (0);
9949
9950 if (FAILED(rc))
9951 {
9952 /* silently try to rename everything back */
9953 if (fileRenamed)
9954 {
9955 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9956 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9957 }
9958 if (dirRenamed)
9959 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9960 }
9961
9962 if (FAILED(rc)) return rc;
9963 }
9964
9965 if (fSettingsFileIsNew)
9966 {
9967 /* create a virgin config file */
9968 int vrc = VINF_SUCCESS;
9969
9970 /* ensure the settings directory exists */
9971 Utf8Str path(mData->m_strConfigFileFull);
9972 path.stripFilename();
9973 if (!RTDirExists(path.c_str()))
9974 {
9975 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9976 if (RT_FAILURE(vrc))
9977 {
9978 return setError(E_FAIL,
9979 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9980 path.c_str(),
9981 vrc);
9982 }
9983 }
9984
9985 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9986 path = Utf8Str(mData->m_strConfigFileFull);
9987 RTFILE f = NIL_RTFILE;
9988 vrc = RTFileOpen(&f, path.c_str(),
9989 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9990 if (RT_FAILURE(vrc))
9991 return setError(E_FAIL,
9992 tr("Could not create the settings file '%s' (%Rrc)"),
9993 path.c_str(),
9994 vrc);
9995 RTFileClose(f);
9996 }
9997
9998 return rc;
9999}
10000
10001/**
10002 * Saves and commits machine data, user data and hardware data.
10003 *
10004 * Note that on failure, the data remains uncommitted.
10005 *
10006 * @a aFlags may combine the following flags:
10007 *
10008 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10009 * Used when saving settings after an operation that makes them 100%
10010 * correspond to the settings from the current snapshot.
10011 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
10012 * #isReallyModified() returns false. This is necessary for cases when we
10013 * change machine data directly, not through the backup()/commit() mechanism.
10014 * - SaveS_Force: settings will be saved without doing a deep compare of the
10015 * settings structures. This is used when this is called because snapshots
10016 * have changed to avoid the overhead of the deep compare.
10017 *
10018 * @note Must be called from under this object's write lock. Locks children for
10019 * writing.
10020 *
10021 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10022 * initialized to false and that will be set to true by this function if
10023 * the caller must invoke VirtualBox::saveSettings() because the global
10024 * settings have changed. This will happen if a machine rename has been
10025 * saved and the global machine and media registries will therefore need
10026 * updating.
10027 */
10028HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
10029 int aFlags /*= 0*/)
10030{
10031 LogFlowThisFuncEnter();
10032
10033 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10034
10035 /* make sure child objects are unable to modify the settings while we are
10036 * saving them */
10037 ensureNoStateDependencies();
10038
10039 AssertReturn(!isSnapshotMachine(),
10040 E_FAIL);
10041
10042 HRESULT rc = S_OK;
10043 bool fNeedsWrite = false;
10044
10045 /* First, prepare to save settings. It will care about renaming the
10046 * settings directory and file if the machine name was changed and about
10047 * creating a new settings file if this is a new machine. */
10048 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
10049 if (FAILED(rc)) return rc;
10050
10051 // keep a pointer to the current settings structures
10052 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10053 settings::MachineConfigFile *pNewConfig = NULL;
10054
10055 try
10056 {
10057 // make a fresh one to have everyone write stuff into
10058 pNewConfig = new settings::MachineConfigFile(NULL);
10059 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10060
10061 // now go and copy all the settings data from COM to the settings structures
10062 // (this calles saveSettings() on all the COM objects in the machine)
10063 copyMachineDataToSettings(*pNewConfig);
10064
10065 if (aFlags & SaveS_ResetCurStateModified)
10066 {
10067 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10068 mData->mCurrentStateModified = FALSE;
10069 fNeedsWrite = true; // always, no need to compare
10070 }
10071 else if (aFlags & SaveS_Force)
10072 {
10073 fNeedsWrite = true; // always, no need to compare
10074 }
10075 else
10076 {
10077 if (!mData->mCurrentStateModified)
10078 {
10079 // do a deep compare of the settings that we just saved with the settings
10080 // previously stored in the config file; this invokes MachineConfigFile::operator==
10081 // which does a deep compare of all the settings, which is expensive but less expensive
10082 // than writing out XML in vain
10083 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10084
10085 // could still be modified if any settings changed
10086 mData->mCurrentStateModified = fAnySettingsChanged;
10087
10088 fNeedsWrite = fAnySettingsChanged;
10089 }
10090 else
10091 fNeedsWrite = true;
10092 }
10093
10094 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10095
10096 if (fNeedsWrite)
10097 // now spit it all out!
10098 pNewConfig->write(mData->m_strConfigFileFull);
10099
10100 mData->pMachineConfigFile = pNewConfig;
10101 delete pOldConfig;
10102 commit();
10103
10104 // after saving settings, we are no longer different from the XML on disk
10105 mData->flModifications = 0;
10106 }
10107 catch (HRESULT err)
10108 {
10109 // we assume that error info is set by the thrower
10110 rc = err;
10111
10112 // restore old config
10113 delete pNewConfig;
10114 mData->pMachineConfigFile = pOldConfig;
10115 }
10116 catch (...)
10117 {
10118 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10119 }
10120
10121 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
10122 {
10123 /* Fire the data change event, even on failure (since we've already
10124 * committed all data). This is done only for SessionMachines because
10125 * mutable Machine instances are always not registered (i.e. private
10126 * to the client process that creates them) and thus don't need to
10127 * inform callbacks. */
10128 if (isSessionMachine())
10129 mParent->onMachineDataChange(mData->mUuid);
10130 }
10131
10132 LogFlowThisFunc(("rc=%08X\n", rc));
10133 LogFlowThisFuncLeave();
10134 return rc;
10135}
10136
10137/**
10138 * Implementation for saving the machine settings into the given
10139 * settings::MachineConfigFile instance. This copies machine extradata
10140 * from the previous machine config file in the instance data, if any.
10141 *
10142 * This gets called from two locations:
10143 *
10144 * -- Machine::saveSettings(), during the regular XML writing;
10145 *
10146 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10147 * exported to OVF and we write the VirtualBox proprietary XML
10148 * into a <vbox:Machine> tag.
10149 *
10150 * This routine fills all the fields in there, including snapshots, *except*
10151 * for the following:
10152 *
10153 * -- fCurrentStateModified. There is some special logic associated with that.
10154 *
10155 * The caller can then call MachineConfigFile::write() or do something else
10156 * with it.
10157 *
10158 * Caller must hold the machine lock!
10159 *
10160 * This throws XML errors and HRESULT, so the caller must have a catch block!
10161 */
10162void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
10163{
10164 // deep copy extradata
10165 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10166
10167 config.uuid = mData->mUuid;
10168
10169 // copy name, description, OS type, teleport, UTC etc.
10170 config.machineUserData = mUserData->s;
10171
10172 // Encode the Icon Override data from Machine and store on config userdata.
10173 com::SafeArray<BYTE> iconByte;
10174 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte));
10175 ssize_t cbData = iconByte.size();
10176 if (cbData > 0)
10177 {
10178 ssize_t cchOut = RTBase64EncodedLength(cbData);
10179 Utf8Str strIconData;
10180 strIconData.reserve(cchOut+1);
10181 int vrc = RTBase64Encode(iconByte.raw(), cbData,
10182 strIconData.mutableRaw(), strIconData.capacity(),
10183 NULL);
10184 if (RT_FAILURE(vrc))
10185 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10186 strIconData.jolt();
10187 config.machineUserData.ovIcon = strIconData;
10188 }
10189 else
10190 config.machineUserData.ovIcon.setNull();
10191
10192 if ( mData->mMachineState == MachineState_Saved
10193 || mData->mMachineState == MachineState_Restoring
10194 // when deleting a snapshot we may or may not have a saved state in the current state,
10195 // so let's not assert here please
10196 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
10197 || mData->mMachineState == MachineState_DeletingSnapshotOnline
10198 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
10199 && (!mSSData->strStateFilePath.isEmpty())
10200 )
10201 )
10202 {
10203 Assert(!mSSData->strStateFilePath.isEmpty());
10204 /* try to make the file name relative to the settings file dir */
10205 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10206 }
10207 else
10208 {
10209 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10210 config.strStateFile.setNull();
10211 }
10212
10213 if (mData->mCurrentSnapshot)
10214 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
10215 else
10216 config.uuidCurrentSnapshot.clear();
10217
10218 config.timeLastStateChange = mData->mLastStateChange;
10219 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10220 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10221
10222 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10223 if (FAILED(rc)) throw rc;
10224
10225 rc = saveStorageControllers(config.storageMachine);
10226 if (FAILED(rc)) throw rc;
10227
10228 // save machine's media registry if this is VirtualBox 4.0 or later
10229 if (config.canHaveOwnMediaRegistry())
10230 {
10231 // determine machine folder
10232 Utf8Str strMachineFolder = getSettingsFileFull();
10233 strMachineFolder.stripFilename();
10234 mParent->saveMediaRegistry(config.mediaRegistry,
10235 getId(), // only media with registry ID == machine UUID
10236 strMachineFolder);
10237 // this throws HRESULT
10238 }
10239
10240 // save snapshots
10241 rc = saveAllSnapshots(config);
10242 if (FAILED(rc)) throw rc;
10243}
10244
10245/**
10246 * Saves all snapshots of the machine into the given machine config file. Called
10247 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10248 * @param config
10249 * @return
10250 */
10251HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
10252{
10253 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10254
10255 HRESULT rc = S_OK;
10256
10257 try
10258 {
10259 config.llFirstSnapshot.clear();
10260
10261 if (mData->mFirstSnapshot)
10262 {
10263 settings::Snapshot snapNew;
10264 config.llFirstSnapshot.push_back(snapNew);
10265
10266 // get reference to the fresh copy of the snapshot on the list and
10267 // work on that copy directly to avoid excessive copying later
10268 settings::Snapshot &snap = config.llFirstSnapshot.front();
10269
10270 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
10271 if (FAILED(rc)) throw rc;
10272 }
10273
10274// if (mType == IsSessionMachine)
10275// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10276
10277 }
10278 catch (HRESULT err)
10279 {
10280 /* we assume that error info is set by the thrower */
10281 rc = err;
10282 }
10283 catch (...)
10284 {
10285 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10286 }
10287
10288 return rc;
10289}
10290
10291/**
10292 * Saves the VM hardware configuration. It is assumed that the
10293 * given node is empty.
10294 *
10295 * @param data Reference to the settings object for the hardware config.
10296 * @param pDbg Pointer to the settings object for the debugging config
10297 * which happens to live in mHWData.
10298 * @param pAutostart Pointer to the settings object for the autostart config
10299 * which happens to live in mHWData.
10300 */
10301HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10302 settings::Autostart *pAutostart)
10303{
10304 HRESULT rc = S_OK;
10305
10306 try
10307 {
10308 /* The hardware version attribute (optional).
10309 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10310 if ( mHWData->mHWVersion == "1"
10311 && mSSData->strStateFilePath.isEmpty()
10312 )
10313 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
10314
10315 data.strVersion = mHWData->mHWVersion;
10316 data.uuid = mHWData->mHardwareUUID;
10317
10318 // CPU
10319 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10320 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
10321 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10322 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10323 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10324 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10325 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10326 data.fPAE = !!mHWData->mPAEEnabled;
10327 data.enmLongMode = mHWData->mLongMode;
10328 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
10329
10330 /* Standard and Extended CPUID leafs. */
10331 data.llCpuIdLeafs.clear();
10332 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
10333 {
10334 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10335 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10336 }
10337 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
10338 {
10339 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10340 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10341 }
10342
10343 data.cCPUs = mHWData->mCPUCount;
10344 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10345 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10346
10347 data.llCpus.clear();
10348 if (data.fCpuHotPlug)
10349 {
10350 for (unsigned idx = 0; idx < data.cCPUs; idx++)
10351 {
10352 if (mHWData->mCPUAttached[idx])
10353 {
10354 settings::Cpu cpu;
10355 cpu.ulId = idx;
10356 data.llCpus.push_back(cpu);
10357 }
10358 }
10359 }
10360
10361 // memory
10362 data.ulMemorySizeMB = mHWData->mMemorySize;
10363 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10364
10365 // firmware
10366 data.firmwareType = mHWData->mFirmwareType;
10367
10368 // HID
10369 data.pointingHIDType = mHWData->mPointingHIDType;
10370 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10371
10372 // chipset
10373 data.chipsetType = mHWData->mChipsetType;
10374
10375 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
10376 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10377
10378 // HPET
10379 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10380
10381 // boot order
10382 data.mapBootOrder.clear();
10383 for (size_t i = 0;
10384 i < RT_ELEMENTS(mHWData->mBootOrder);
10385 ++i)
10386 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10387
10388 // display
10389 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10390 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10391 data.cMonitors = mHWData->mMonitorCount;
10392 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10393 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10394 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10395 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10396 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10397 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10398 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10399 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++)
10400 {
10401 if (mHWData->maVideoCaptureScreens[i])
10402 ASMBitSet(&data.u64VideoCaptureScreens, i);
10403 else
10404 ASMBitClear(&data.u64VideoCaptureScreens, i);
10405 }
10406 /* store relative video capture file if possible */
10407 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10408
10409 /* VRDEServer settings (optional) */
10410 rc = mVRDEServer->saveSettings(data.vrdeSettings);
10411 if (FAILED(rc)) throw rc;
10412
10413 /* BIOS (required) */
10414 rc = mBIOSSettings->saveSettings(data.biosSettings);
10415 if (FAILED(rc)) throw rc;
10416
10417 /* USB Controller (required) */
10418 rc = mUSBController->saveSettings(data.usbController);
10419 if (FAILED(rc)) throw rc;
10420
10421 /* USB device filters (required) */
10422 rc = mUSBDeviceFilters->saveSettings(data.usbController);
10423 if (FAILED(rc)) throw rc;
10424
10425 /* Network adapters (required) */
10426 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10427 data.llNetworkAdapters.clear();
10428 /* Write out only the nominal number of network adapters for this
10429 * chipset type. Since Machine::commit() hasn't been called there
10430 * may be extra NIC settings in the vector. */
10431 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10432 {
10433 settings::NetworkAdapter nic;
10434 nic.ulSlot = slot;
10435 /* paranoia check... must not be NULL, but must not crash either. */
10436 if (mNetworkAdapters[slot])
10437 {
10438 rc = mNetworkAdapters[slot]->saveSettings(nic);
10439 if (FAILED(rc)) throw rc;
10440
10441 data.llNetworkAdapters.push_back(nic);
10442 }
10443 }
10444
10445 /* Serial ports */
10446 data.llSerialPorts.clear();
10447 for (ULONG slot = 0;
10448 slot < RT_ELEMENTS(mSerialPorts);
10449 ++slot)
10450 {
10451 settings::SerialPort s;
10452 s.ulSlot = slot;
10453 rc = mSerialPorts[slot]->saveSettings(s);
10454 if (FAILED(rc)) return rc;
10455
10456 data.llSerialPorts.push_back(s);
10457 }
10458
10459 /* Parallel ports */
10460 data.llParallelPorts.clear();
10461 for (ULONG slot = 0;
10462 slot < RT_ELEMENTS(mParallelPorts);
10463 ++slot)
10464 {
10465 settings::ParallelPort p;
10466 p.ulSlot = slot;
10467 rc = mParallelPorts[slot]->saveSettings(p);
10468 if (FAILED(rc)) return rc;
10469
10470 data.llParallelPorts.push_back(p);
10471 }
10472
10473 /* Audio adapter */
10474 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10475 if (FAILED(rc)) return rc;
10476
10477 /* Shared folders */
10478 data.llSharedFolders.clear();
10479 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10480 it != mHWData->mSharedFolders.end();
10481 ++it)
10482 {
10483 SharedFolder *pSF = *it;
10484 AutoCaller sfCaller(pSF);
10485 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10486 settings::SharedFolder sf;
10487 sf.strName = pSF->getName();
10488 sf.strHostPath = pSF->getHostPath();
10489 sf.fWritable = !!pSF->isWritable();
10490 sf.fAutoMount = !!pSF->isAutoMounted();
10491
10492 data.llSharedFolders.push_back(sf);
10493 }
10494
10495 // clipboard
10496 data.clipboardMode = mHWData->mClipboardMode;
10497
10498 // drag'n'drop
10499 data.dragAndDropMode = mHWData->mDragAndDropMode;
10500
10501 /* Guest */
10502 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10503
10504 // IO settings
10505 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10506 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10507
10508 /* BandwidthControl (required) */
10509 rc = mBandwidthControl->saveSettings(data.ioSettings);
10510 if (FAILED(rc)) throw rc;
10511
10512 /* Host PCI devices */
10513 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10514 it != mHWData->mPCIDeviceAssignments.end();
10515 ++it)
10516 {
10517 ComObjPtr<PCIDeviceAttachment> pda = *it;
10518 settings::HostPCIDeviceAttachment hpda;
10519
10520 rc = pda->saveSettings(hpda);
10521 if (FAILED(rc)) throw rc;
10522
10523 data.pciAttachments.push_back(hpda);
10524 }
10525
10526
10527 // guest properties
10528 data.llGuestProperties.clear();
10529#ifdef VBOX_WITH_GUEST_PROPS
10530 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10531 it != mHWData->mGuestProperties.end();
10532 ++it)
10533 {
10534 HWData::GuestProperty property = it->second;
10535
10536 /* Remove transient guest properties at shutdown unless we
10537 * are saving state */
10538 if ( ( mData->mMachineState == MachineState_PoweredOff
10539 || mData->mMachineState == MachineState_Aborted
10540 || mData->mMachineState == MachineState_Teleported)
10541 && ( property.mFlags & guestProp::TRANSIENT
10542 || property.mFlags & guestProp::TRANSRESET))
10543 continue;
10544 settings::GuestProperty prop;
10545 prop.strName = it->first;
10546 prop.strValue = property.strValue;
10547 prop.timestamp = property.mTimestamp;
10548 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10549 guestProp::writeFlags(property.mFlags, szFlags);
10550 prop.strFlags = szFlags;
10551
10552 data.llGuestProperties.push_back(prop);
10553 }
10554
10555 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10556 /* I presume this doesn't require a backup(). */
10557 mData->mGuestPropertiesModified = FALSE;
10558#endif /* VBOX_WITH_GUEST_PROPS defined */
10559
10560 *pDbg = mHWData->mDebugging;
10561 *pAutostart = mHWData->mAutostart;
10562
10563 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10564 }
10565 catch(std::bad_alloc &)
10566 {
10567 return E_OUTOFMEMORY;
10568 }
10569
10570 AssertComRC(rc);
10571 return rc;
10572}
10573
10574/**
10575 * Saves the storage controller configuration.
10576 *
10577 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10578 */
10579HRESULT Machine::saveStorageControllers(settings::Storage &data)
10580{
10581 data.llStorageControllers.clear();
10582
10583 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10584 it != mStorageControllers->end();
10585 ++it)
10586 {
10587 HRESULT rc;
10588 ComObjPtr<StorageController> pCtl = *it;
10589
10590 settings::StorageController ctl;
10591 ctl.strName = pCtl->getName();
10592 ctl.controllerType = pCtl->getControllerType();
10593 ctl.storageBus = pCtl->getStorageBus();
10594 ctl.ulInstance = pCtl->getInstance();
10595 ctl.fBootable = pCtl->getBootable();
10596
10597 /* Save the port count. */
10598 ULONG portCount;
10599 rc = pCtl->COMGETTER(PortCount)(&portCount);
10600 ComAssertComRCRet(rc, rc);
10601 ctl.ulPortCount = portCount;
10602
10603 /* Save fUseHostIOCache */
10604 BOOL fUseHostIOCache;
10605 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10606 ComAssertComRCRet(rc, rc);
10607 ctl.fUseHostIOCache = !!fUseHostIOCache;
10608
10609 /* Save IDE emulation settings. */
10610 if (ctl.controllerType == StorageControllerType_IntelAhci)
10611 {
10612 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10613 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10614 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10615 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10616 )
10617 ComAssertComRCRet(rc, rc);
10618 }
10619
10620 /* save the devices now. */
10621 rc = saveStorageDevices(pCtl, ctl);
10622 ComAssertComRCRet(rc, rc);
10623
10624 data.llStorageControllers.push_back(ctl);
10625 }
10626
10627 return S_OK;
10628}
10629
10630/**
10631 * Saves the hard disk configuration.
10632 */
10633HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10634 settings::StorageController &data)
10635{
10636 MediaData::AttachmentList atts;
10637
10638 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10639 if (FAILED(rc)) return rc;
10640
10641 data.llAttachedDevices.clear();
10642 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10643 it != atts.end();
10644 ++it)
10645 {
10646 settings::AttachedDevice dev;
10647
10648 MediumAttachment *pAttach = *it;
10649 Medium *pMedium = pAttach->getMedium();
10650
10651 dev.deviceType = pAttach->getType();
10652 dev.lPort = pAttach->getPort();
10653 dev.lDevice = pAttach->getDevice();
10654 if (pMedium)
10655 {
10656 if (pMedium->isHostDrive())
10657 dev.strHostDriveSrc = pMedium->getLocationFull();
10658 else
10659 dev.uuid = pMedium->getId();
10660 dev.fPassThrough = pAttach->getPassthrough();
10661 dev.fTempEject = pAttach->getTempEject();
10662 dev.fNonRotational = pAttach->getNonRotational();
10663 dev.fDiscard = pAttach->getDiscard();
10664 }
10665
10666 dev.strBwGroup = pAttach->getBandwidthGroup();
10667
10668 data.llAttachedDevices.push_back(dev);
10669 }
10670
10671 return S_OK;
10672}
10673
10674/**
10675 * Saves machine state settings as defined by aFlags
10676 * (SaveSTS_* values).
10677 *
10678 * @param aFlags Combination of SaveSTS_* flags.
10679 *
10680 * @note Locks objects for writing.
10681 */
10682HRESULT Machine::saveStateSettings(int aFlags)
10683{
10684 if (aFlags == 0)
10685 return S_OK;
10686
10687 AutoCaller autoCaller(this);
10688 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10689
10690 /* This object's write lock is also necessary to serialize file access
10691 * (prevent concurrent reads and writes) */
10692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10693
10694 HRESULT rc = S_OK;
10695
10696 Assert(mData->pMachineConfigFile);
10697
10698 try
10699 {
10700 if (aFlags & SaveSTS_CurStateModified)
10701 mData->pMachineConfigFile->fCurrentStateModified = true;
10702
10703 if (aFlags & SaveSTS_StateFilePath)
10704 {
10705 if (!mSSData->strStateFilePath.isEmpty())
10706 /* try to make the file name relative to the settings file dir */
10707 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10708 else
10709 mData->pMachineConfigFile->strStateFile.setNull();
10710 }
10711
10712 if (aFlags & SaveSTS_StateTimeStamp)
10713 {
10714 Assert( mData->mMachineState != MachineState_Aborted
10715 || mSSData->strStateFilePath.isEmpty());
10716
10717 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10718
10719 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10720//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10721 }
10722
10723 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10724 }
10725 catch (...)
10726 {
10727 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10728 }
10729
10730 return rc;
10731}
10732
10733/**
10734 * Ensures that the given medium is added to a media registry. If this machine
10735 * was created with 4.0 or later, then the machine registry is used. Otherwise
10736 * the global VirtualBox media registry is used.
10737 *
10738 * Caller must NOT hold machine lock, media tree or any medium locks!
10739 *
10740 * @param pMedium
10741 */
10742void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10743{
10744 /* Paranoia checks: do not hold machine or media tree locks. */
10745 AssertReturnVoid(!isWriteLockOnCurrentThread());
10746 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10747
10748 ComObjPtr<Medium> pBase;
10749 {
10750 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10751 pBase = pMedium->getBase();
10752 }
10753
10754 /* Paranoia checks: do not hold medium locks. */
10755 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10756 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10757
10758 // decide which medium registry to use now that the medium is attached:
10759 Guid uuid;
10760 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10761 // machine XML is VirtualBox 4.0 or higher:
10762 uuid = getId(); // machine UUID
10763 else
10764 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10765
10766 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10767 mParent->markRegistryModified(uuid);
10768
10769 /* For more complex hard disk structures it can happen that the base
10770 * medium isn't yet associated with any medium registry. Do that now. */
10771 if (pMedium != pBase)
10772 {
10773 if (pBase->addRegistry(uuid, true /* fRecurse */))
10774 mParent->markRegistryModified(uuid);
10775 }
10776}
10777
10778/**
10779 * Creates differencing hard disks for all normal hard disks attached to this
10780 * machine and a new set of attachments to refer to created disks.
10781 *
10782 * Used when taking a snapshot or when deleting the current state. Gets called
10783 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10784 *
10785 * This method assumes that mMediaData contains the original hard disk attachments
10786 * it needs to create diffs for. On success, these attachments will be replaced
10787 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10788 * called to delete created diffs which will also rollback mMediaData and restore
10789 * whatever was backed up before calling this method.
10790 *
10791 * Attachments with non-normal hard disks are left as is.
10792 *
10793 * If @a aOnline is @c false then the original hard disks that require implicit
10794 * diffs will be locked for reading. Otherwise it is assumed that they are
10795 * already locked for writing (when the VM was started). Note that in the latter
10796 * case it is responsibility of the caller to lock the newly created diffs for
10797 * writing if this method succeeds.
10798 *
10799 * @param aProgress Progress object to run (must contain at least as
10800 * many operations left as the number of hard disks
10801 * attached).
10802 * @param aOnline Whether the VM was online prior to this operation.
10803 *
10804 * @note The progress object is not marked as completed, neither on success nor
10805 * on failure. This is a responsibility of the caller.
10806 *
10807 * @note Locks this object and the media tree for writing.
10808 */
10809HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10810 ULONG aWeight,
10811 bool aOnline)
10812{
10813 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10814
10815 AutoCaller autoCaller(this);
10816 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10817
10818 AutoMultiWriteLock2 alock(this->lockHandle(),
10819 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10820
10821 /* must be in a protective state because we release the lock below */
10822 AssertReturn( mData->mMachineState == MachineState_Saving
10823 || mData->mMachineState == MachineState_LiveSnapshotting
10824 || mData->mMachineState == MachineState_RestoringSnapshot
10825 || mData->mMachineState == MachineState_DeletingSnapshot
10826 , E_FAIL);
10827
10828 HRESULT rc = S_OK;
10829
10830 // use appropriate locked media map (online or offline)
10831 MediumLockListMap lockedMediaOffline;
10832 MediumLockListMap *lockedMediaMap;
10833 if (aOnline)
10834 lockedMediaMap = &mData->mSession.mLockedMedia;
10835 else
10836 lockedMediaMap = &lockedMediaOffline;
10837
10838 try
10839 {
10840 if (!aOnline)
10841 {
10842 /* lock all attached hard disks early to detect "in use"
10843 * situations before creating actual diffs */
10844 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10845 it != mMediaData->mAttachments.end();
10846 ++it)
10847 {
10848 MediumAttachment* pAtt = *it;
10849 if (pAtt->getType() == DeviceType_HardDisk)
10850 {
10851 Medium* pMedium = pAtt->getMedium();
10852 Assert(pMedium);
10853
10854 MediumLockList *pMediumLockList(new MediumLockList());
10855 alock.release();
10856 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10857 false /* fMediumLockWrite */,
10858 NULL,
10859 *pMediumLockList);
10860 alock.acquire();
10861 if (FAILED(rc))
10862 {
10863 delete pMediumLockList;
10864 throw rc;
10865 }
10866 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10867 if (FAILED(rc))
10868 {
10869 throw setError(rc,
10870 tr("Collecting locking information for all attached media failed"));
10871 }
10872 }
10873 }
10874
10875 /* Now lock all media. If this fails, nothing is locked. */
10876 alock.release();
10877 rc = lockedMediaMap->Lock();
10878 alock.acquire();
10879 if (FAILED(rc))
10880 {
10881 throw setError(rc,
10882 tr("Locking of attached media failed"));
10883 }
10884 }
10885
10886 /* remember the current list (note that we don't use backup() since
10887 * mMediaData may be already backed up) */
10888 MediaData::AttachmentList atts = mMediaData->mAttachments;
10889
10890 /* start from scratch */
10891 mMediaData->mAttachments.clear();
10892
10893 /* go through remembered attachments and create diffs for normal hard
10894 * disks and attach them */
10895 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10896 it != atts.end();
10897 ++it)
10898 {
10899 MediumAttachment* pAtt = *it;
10900
10901 DeviceType_T devType = pAtt->getType();
10902 Medium* pMedium = pAtt->getMedium();
10903
10904 if ( devType != DeviceType_HardDisk
10905 || pMedium == NULL
10906 || pMedium->getType() != MediumType_Normal)
10907 {
10908 /* copy the attachment as is */
10909
10910 /** @todo the progress object created in Console::TakeSnaphot
10911 * only expects operations for hard disks. Later other
10912 * device types need to show up in the progress as well. */
10913 if (devType == DeviceType_HardDisk)
10914 {
10915 if (pMedium == NULL)
10916 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10917 aWeight); // weight
10918 else
10919 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10920 pMedium->getBase()->getName().c_str()).raw(),
10921 aWeight); // weight
10922 }
10923
10924 mMediaData->mAttachments.push_back(pAtt);
10925 continue;
10926 }
10927
10928 /* need a diff */
10929 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10930 pMedium->getBase()->getName().c_str()).raw(),
10931 aWeight); // weight
10932
10933 Utf8Str strFullSnapshotFolder;
10934 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10935
10936 ComObjPtr<Medium> diff;
10937 diff.createObject();
10938 // store the diff in the same registry as the parent
10939 // (this cannot fail here because we can't create implicit diffs for
10940 // unregistered images)
10941 Guid uuidRegistryParent;
10942 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10943 Assert(fInRegistry); NOREF(fInRegistry);
10944 rc = diff->init(mParent,
10945 pMedium->getPreferredDiffFormat(),
10946 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10947 uuidRegistryParent);
10948 if (FAILED(rc)) throw rc;
10949
10950 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10951 * the push_back? Looks like we're going to release medium with the
10952 * wrong kind of lock (general issue with if we fail anywhere at all)
10953 * and an orphaned VDI in the snapshots folder. */
10954
10955 /* update the appropriate lock list */
10956 MediumLockList *pMediumLockList;
10957 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10958 AssertComRCThrowRC(rc);
10959 if (aOnline)
10960 {
10961 alock.release();
10962 /* The currently attached medium will be read-only, change
10963 * the lock type to read. */
10964 rc = pMediumLockList->Update(pMedium, false);
10965 alock.acquire();
10966 AssertComRCThrowRC(rc);
10967 }
10968
10969 /* release the locks before the potentially lengthy operation */
10970 alock.release();
10971 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10972 pMediumLockList,
10973 NULL /* aProgress */,
10974 true /* aWait */);
10975 alock.acquire();
10976 if (FAILED(rc)) throw rc;
10977
10978 /* actual lock list update is done in Medium::commitMedia */
10979
10980 rc = diff->addBackReference(mData->mUuid);
10981 AssertComRCThrowRC(rc);
10982
10983 /* add a new attachment */
10984 ComObjPtr<MediumAttachment> attachment;
10985 attachment.createObject();
10986 rc = attachment->init(this,
10987 diff,
10988 pAtt->getControllerName(),
10989 pAtt->getPort(),
10990 pAtt->getDevice(),
10991 DeviceType_HardDisk,
10992 true /* aImplicit */,
10993 false /* aPassthrough */,
10994 false /* aTempEject */,
10995 pAtt->getNonRotational(),
10996 pAtt->getDiscard(),
10997 pAtt->getBandwidthGroup());
10998 if (FAILED(rc)) throw rc;
10999
11000 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11001 AssertComRCThrowRC(rc);
11002 mMediaData->mAttachments.push_back(attachment);
11003 }
11004 }
11005 catch (HRESULT aRC) { rc = aRC; }
11006
11007 /* unlock all hard disks we locked when there is no VM */
11008 if (!aOnline)
11009 {
11010 ErrorInfoKeeper eik;
11011
11012 HRESULT rc1 = lockedMediaMap->Clear();
11013 AssertComRC(rc1);
11014 }
11015
11016 return rc;
11017}
11018
11019/**
11020 * Deletes implicit differencing hard disks created either by
11021 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
11022 *
11023 * Note that to delete hard disks created by #AttachDevice() this method is
11024 * called from #fixupMedia() when the changes are rolled back.
11025 *
11026 * @note Locks this object and the media tree for writing.
11027 */
11028HRESULT Machine::deleteImplicitDiffs(bool aOnline)
11029{
11030 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11031
11032 AutoCaller autoCaller(this);
11033 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11034
11035 AutoMultiWriteLock2 alock(this->lockHandle(),
11036 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11037
11038 /* We absolutely must have backed up state. */
11039 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
11040
11041 /* Check if there are any implicitly created diff images. */
11042 bool fImplicitDiffs = false;
11043 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11044 it != mMediaData->mAttachments.end();
11045 ++it)
11046 {
11047 const ComObjPtr<MediumAttachment> &pAtt = *it;
11048 if (pAtt->isImplicit())
11049 {
11050 fImplicitDiffs = true;
11051 break;
11052 }
11053 }
11054 /* If there is nothing to do, leave early. This saves lots of image locking
11055 * effort. It also avoids a MachineStateChanged event without real reason.
11056 * This is important e.g. when loading a VM config, because there should be
11057 * no events. Otherwise API clients can become thoroughly confused for
11058 * inaccessible VMs (the code for loading VM configs uses this method for
11059 * cleanup if the config makes no sense), as they take such events as an
11060 * indication that the VM is alive, and they would force the VM config to
11061 * be reread, leading to an endless loop. */
11062 if (!fImplicitDiffs)
11063 return S_OK;
11064
11065 HRESULT rc = S_OK;
11066 MachineState_T oldState = mData->mMachineState;
11067
11068 /* will release the lock before the potentially lengthy operation,
11069 * so protect with the special state (unless already protected) */
11070 if ( oldState != MachineState_Saving
11071 && oldState != MachineState_LiveSnapshotting
11072 && oldState != MachineState_RestoringSnapshot
11073 && oldState != MachineState_DeletingSnapshot
11074 && oldState != MachineState_DeletingSnapshotOnline
11075 && oldState != MachineState_DeletingSnapshotPaused
11076 )
11077 setMachineState(MachineState_SettingUp);
11078
11079 // use appropriate locked media map (online or offline)
11080 MediumLockListMap lockedMediaOffline;
11081 MediumLockListMap *lockedMediaMap;
11082 if (aOnline)
11083 lockedMediaMap = &mData->mSession.mLockedMedia;
11084 else
11085 lockedMediaMap = &lockedMediaOffline;
11086
11087 try
11088 {
11089 if (!aOnline)
11090 {
11091 /* lock all attached hard disks early to detect "in use"
11092 * situations before deleting actual diffs */
11093 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11094 it != mMediaData->mAttachments.end();
11095 ++it)
11096 {
11097 MediumAttachment* pAtt = *it;
11098 if (pAtt->getType() == DeviceType_HardDisk)
11099 {
11100 Medium* pMedium = pAtt->getMedium();
11101 Assert(pMedium);
11102
11103 MediumLockList *pMediumLockList(new MediumLockList());
11104 alock.release();
11105 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
11106 false /* fMediumLockWrite */,
11107 NULL,
11108 *pMediumLockList);
11109 alock.acquire();
11110
11111 if (FAILED(rc))
11112 {
11113 delete pMediumLockList;
11114 throw rc;
11115 }
11116
11117 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11118 if (FAILED(rc))
11119 throw rc;
11120 }
11121 }
11122
11123 if (FAILED(rc))
11124 throw rc;
11125 } // end of offline
11126
11127 /* Lock lists are now up to date and include implicitly created media */
11128
11129 /* Go through remembered attachments and delete all implicitly created
11130 * diffs and fix up the attachment information */
11131 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11132 MediaData::AttachmentList implicitAtts;
11133 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11134 it != mMediaData->mAttachments.end();
11135 ++it)
11136 {
11137 ComObjPtr<MediumAttachment> pAtt = *it;
11138 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11139 if (pMedium.isNull())
11140 continue;
11141
11142 // Implicit attachments go on the list for deletion and back references are removed.
11143 if (pAtt->isImplicit())
11144 {
11145 /* Deassociate and mark for deletion */
11146 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
11147 rc = pMedium->removeBackReference(mData->mUuid);
11148 if (FAILED(rc))
11149 throw rc;
11150 implicitAtts.push_back(pAtt);
11151 continue;
11152 }
11153
11154 /* Was this medium attached before? */
11155 if (!findAttachment(oldAtts, pMedium))
11156 {
11157 /* no: de-associate */
11158 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
11159 rc = pMedium->removeBackReference(mData->mUuid);
11160 if (FAILED(rc))
11161 throw rc;
11162 continue;
11163 }
11164 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
11165 }
11166
11167 /* If there are implicit attachments to delete, throw away the lock
11168 * map contents (which will unlock all media) since the medium
11169 * attachments will be rolled back. Below we need to completely
11170 * recreate the lock map anyway since it is infinitely complex to
11171 * do this incrementally (would need reconstructing each attachment
11172 * change, which would be extremely hairy). */
11173 if (implicitAtts.size() != 0)
11174 {
11175 ErrorInfoKeeper eik;
11176
11177 HRESULT rc1 = lockedMediaMap->Clear();
11178 AssertComRC(rc1);
11179 }
11180
11181 /* rollback hard disk changes */
11182 mMediaData.rollback();
11183
11184 MultiResult mrc(S_OK);
11185
11186 // Delete unused implicit diffs.
11187 if (implicitAtts.size() != 0)
11188 {
11189 alock.release();
11190
11191 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
11192 it != implicitAtts.end();
11193 ++it)
11194 {
11195 // Remove medium associated with this attachment.
11196 ComObjPtr<MediumAttachment> pAtt = *it;
11197 Assert(pAtt);
11198 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
11199 ComObjPtr<Medium> pMedium = pAtt->getMedium();
11200 Assert(pMedium);
11201
11202 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11203 // continue on delete failure, just collect error messages
11204 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
11205 mrc = rc;
11206 }
11207
11208 alock.acquire();
11209
11210 /* if there is a VM recreate media lock map as mentioned above,
11211 * otherwise it is a waste of time and we leave things unlocked */
11212 if (aOnline)
11213 {
11214 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11215 /* must never be NULL, but better safe than sorry */
11216 if (!pMachine.isNull())
11217 {
11218 alock.release();
11219 rc = mData->mSession.mMachine->lockMedia();
11220 alock.acquire();
11221 if (FAILED(rc))
11222 throw rc;
11223 }
11224 }
11225 }
11226 }
11227 catch (HRESULT aRC) {rc = aRC;}
11228
11229 if (mData->mMachineState == MachineState_SettingUp)
11230 setMachineState(oldState);
11231
11232 /* unlock all hard disks we locked when there is no VM */
11233 if (!aOnline)
11234 {
11235 ErrorInfoKeeper eik;
11236
11237 HRESULT rc1 = lockedMediaMap->Clear();
11238 AssertComRC(rc1);
11239 }
11240
11241 return rc;
11242}
11243
11244
11245/**
11246 * Looks through the given list of media attachments for one with the given parameters
11247 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11248 * can be searched as well if needed.
11249 *
11250 * @param list
11251 * @param aControllerName
11252 * @param aControllerPort
11253 * @param aDevice
11254 * @return
11255 */
11256MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11257 IN_BSTR aControllerName,
11258 LONG aControllerPort,
11259 LONG aDevice)
11260{
11261 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11262 it != ll.end();
11263 ++it)
11264 {
11265 MediumAttachment *pAttach = *it;
11266 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
11267 return pAttach;
11268 }
11269
11270 return NULL;
11271}
11272
11273/**
11274 * Looks through the given list of media attachments for one with the given parameters
11275 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11276 * can be searched as well if needed.
11277 *
11278 * @param list
11279 * @param aControllerName
11280 * @param aControllerPort
11281 * @param aDevice
11282 * @return
11283 */
11284MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11285 ComObjPtr<Medium> pMedium)
11286{
11287 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11288 it != ll.end();
11289 ++it)
11290 {
11291 MediumAttachment *pAttach = *it;
11292 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11293 if (pMediumThis == pMedium)
11294 return pAttach;
11295 }
11296
11297 return NULL;
11298}
11299
11300/**
11301 * Looks through the given list of media attachments for one with the given parameters
11302 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11303 * can be searched as well if needed.
11304 *
11305 * @param list
11306 * @param aControllerName
11307 * @param aControllerPort
11308 * @param aDevice
11309 * @return
11310 */
11311MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
11312 Guid &id)
11313{
11314 for (MediaData::AttachmentList::const_iterator it = ll.begin();
11315 it != ll.end();
11316 ++it)
11317 {
11318 MediumAttachment *pAttach = *it;
11319 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
11320 if (pMediumThis->getId() == id)
11321 return pAttach;
11322 }
11323
11324 return NULL;
11325}
11326
11327/**
11328 * Main implementation for Machine::DetachDevice. This also gets called
11329 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11330 *
11331 * @param pAttach Medium attachment to detach.
11332 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11333 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
11334 * @return
11335 */
11336HRESULT Machine::detachDevice(MediumAttachment *pAttach,
11337 AutoWriteLock &writeLock,
11338 Snapshot *pSnapshot)
11339{
11340 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
11341 DeviceType_T mediumType = pAttach->getType();
11342
11343 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
11344
11345 if (pAttach->isImplicit())
11346 {
11347 /* attempt to implicitly delete the implicitly created diff */
11348
11349 /// @todo move the implicit flag from MediumAttachment to Medium
11350 /// and forbid any hard disk operation when it is implicit. Or maybe
11351 /// a special media state for it to make it even more simple.
11352
11353 Assert(mMediaData.isBackedUp());
11354
11355 /* will release the lock before the potentially lengthy operation, so
11356 * protect with the special state */
11357 MachineState_T oldState = mData->mMachineState;
11358 setMachineState(MachineState_SettingUp);
11359
11360 writeLock.release();
11361
11362 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
11363 true /*aWait*/);
11364
11365 writeLock.acquire();
11366
11367 setMachineState(oldState);
11368
11369 if (FAILED(rc)) return rc;
11370 }
11371
11372 setModified(IsModified_Storage);
11373 mMediaData.backup();
11374 mMediaData->mAttachments.remove(pAttach);
11375
11376 if (!oldmedium.isNull())
11377 {
11378 // if this is from a snapshot, do not defer detachment to commitMedia()
11379 if (pSnapshot)
11380 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
11381 // else if non-hard disk media, do not defer detachment to commitMedia() either
11382 else if (mediumType != DeviceType_HardDisk)
11383 oldmedium->removeBackReference(mData->mUuid);
11384 }
11385
11386 return S_OK;
11387}
11388
11389/**
11390 * Goes thru all media of the given list and
11391 *
11392 * 1) calls detachDevice() on each of them for this machine and
11393 * 2) adds all Medium objects found in the process to the given list,
11394 * depending on cleanupMode.
11395 *
11396 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11397 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11398 * media to the list.
11399 *
11400 * This gets called from Machine::Unregister, both for the actual Machine and
11401 * the SnapshotMachine objects that might be found in the snapshots.
11402 *
11403 * Requires caller and locking. The machine lock must be passed in because it
11404 * will be passed on to detachDevice which needs it for temporary unlocking.
11405 *
11406 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11407 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11408 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11409 * otherwise no media get added.
11410 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11411 * @return
11412 */
11413HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11414 Snapshot *pSnapshot,
11415 CleanupMode_T cleanupMode,
11416 MediaList &llMedia)
11417{
11418 Assert(isWriteLockOnCurrentThread());
11419
11420 HRESULT rc;
11421
11422 // make a temporary list because detachDevice invalidates iterators into
11423 // mMediaData->mAttachments
11424 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11425
11426 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11427 it != llAttachments2.end();
11428 ++it)
11429 {
11430 ComObjPtr<MediumAttachment> &pAttach = *it;
11431 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11432
11433 if (!pMedium.isNull())
11434 {
11435 AutoCaller mac(pMedium);
11436 if (FAILED(mac.rc())) return mac.rc();
11437 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11438 DeviceType_T devType = pMedium->getDeviceType();
11439 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11440 && devType == DeviceType_HardDisk)
11441 || (cleanupMode == CleanupMode_Full)
11442 )
11443 {
11444 llMedia.push_back(pMedium);
11445 ComObjPtr<Medium> pParent = pMedium->getParent();
11446 /*
11447 * Search for medias which are not attached to any machine, but
11448 * in the chain to an attached disk. Mediums are only consided
11449 * if they are:
11450 * - have only one child
11451 * - no references to any machines
11452 * - are of normal medium type
11453 */
11454 while (!pParent.isNull())
11455 {
11456 AutoCaller mac1(pParent);
11457 if (FAILED(mac1.rc())) return mac1.rc();
11458 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11459 if (pParent->getChildren().size() == 1)
11460 {
11461 if ( pParent->getMachineBackRefCount() == 0
11462 && pParent->getType() == MediumType_Normal
11463 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11464 llMedia.push_back(pParent);
11465 }
11466 else
11467 break;
11468 pParent = pParent->getParent();
11469 }
11470 }
11471 }
11472
11473 // real machine: then we need to use the proper method
11474 rc = detachDevice(pAttach, writeLock, pSnapshot);
11475
11476 if (FAILED(rc))
11477 return rc;
11478 }
11479
11480 return S_OK;
11481}
11482
11483/**
11484 * Perform deferred hard disk detachments.
11485 *
11486 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11487 * backed up).
11488 *
11489 * If @a aOnline is @c true then this method will also unlock the old hard disks
11490 * for which the new implicit diffs were created and will lock these new diffs for
11491 * writing.
11492 *
11493 * @param aOnline Whether the VM was online prior to this operation.
11494 *
11495 * @note Locks this object for writing!
11496 */
11497void Machine::commitMedia(bool aOnline /*= false*/)
11498{
11499 AutoCaller autoCaller(this);
11500 AssertComRCReturnVoid(autoCaller.rc());
11501
11502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11503
11504 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11505
11506 HRESULT rc = S_OK;
11507
11508 /* no attach/detach operations -- nothing to do */
11509 if (!mMediaData.isBackedUp())
11510 return;
11511
11512 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11513 bool fMediaNeedsLocking = false;
11514
11515 /* enumerate new attachments */
11516 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11517 it != mMediaData->mAttachments.end();
11518 ++it)
11519 {
11520 MediumAttachment *pAttach = *it;
11521
11522 pAttach->commit();
11523
11524 Medium* pMedium = pAttach->getMedium();
11525 bool fImplicit = pAttach->isImplicit();
11526
11527 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11528 (pMedium) ? pMedium->getName().c_str() : "NULL",
11529 fImplicit));
11530
11531 /** @todo convert all this Machine-based voodoo to MediumAttachment
11532 * based commit logic. */
11533 if (fImplicit)
11534 {
11535 /* convert implicit attachment to normal */
11536 pAttach->setImplicit(false);
11537
11538 if ( aOnline
11539 && pMedium
11540 && pAttach->getType() == DeviceType_HardDisk
11541 )
11542 {
11543 ComObjPtr<Medium> parent = pMedium->getParent();
11544 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11545
11546 /* update the appropriate lock list */
11547 MediumLockList *pMediumLockList;
11548 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11549 AssertComRC(rc);
11550 if (pMediumLockList)
11551 {
11552 /* unlock if there's a need to change the locking */
11553 if (!fMediaNeedsLocking)
11554 {
11555 rc = mData->mSession.mLockedMedia.Unlock();
11556 AssertComRC(rc);
11557 fMediaNeedsLocking = true;
11558 }
11559 rc = pMediumLockList->Update(parent, false);
11560 AssertComRC(rc);
11561 rc = pMediumLockList->Append(pMedium, true);
11562 AssertComRC(rc);
11563 }
11564 }
11565
11566 continue;
11567 }
11568
11569 if (pMedium)
11570 {
11571 /* was this medium attached before? */
11572 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11573 oldIt != oldAtts.end();
11574 ++oldIt)
11575 {
11576 MediumAttachment *pOldAttach = *oldIt;
11577 if (pOldAttach->getMedium() == pMedium)
11578 {
11579 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11580
11581 /* yes: remove from old to avoid de-association */
11582 oldAtts.erase(oldIt);
11583 break;
11584 }
11585 }
11586 }
11587 }
11588
11589 /* enumerate remaining old attachments and de-associate from the
11590 * current machine state */
11591 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11592 it != oldAtts.end();
11593 ++it)
11594 {
11595 MediumAttachment *pAttach = *it;
11596 Medium* pMedium = pAttach->getMedium();
11597
11598 /* Detach only hard disks, since DVD/floppy media is detached
11599 * instantly in MountMedium. */
11600 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11601 {
11602 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11603
11604 /* now de-associate from the current machine state */
11605 rc = pMedium->removeBackReference(mData->mUuid);
11606 AssertComRC(rc);
11607
11608 if (aOnline)
11609 {
11610 /* unlock since medium is not used anymore */
11611 MediumLockList *pMediumLockList;
11612 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11613 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11614 {
11615 /* this happens for online snapshots, there the attachment
11616 * is changing, but only to a diff image created under
11617 * the old one, so there is no separate lock list */
11618 Assert(!pMediumLockList);
11619 }
11620 else
11621 {
11622 AssertComRC(rc);
11623 if (pMediumLockList)
11624 {
11625 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11626 AssertComRC(rc);
11627 }
11628 }
11629 }
11630 }
11631 }
11632
11633 /* take media locks again so that the locking state is consistent */
11634 if (fMediaNeedsLocking)
11635 {
11636 Assert(aOnline);
11637 rc = mData->mSession.mLockedMedia.Lock();
11638 AssertComRC(rc);
11639 }
11640
11641 /* commit the hard disk changes */
11642 mMediaData.commit();
11643
11644 if (isSessionMachine())
11645 {
11646 /*
11647 * Update the parent machine to point to the new owner.
11648 * This is necessary because the stored parent will point to the
11649 * session machine otherwise and cause crashes or errors later
11650 * when the session machine gets invalid.
11651 */
11652 /** @todo Change the MediumAttachment class to behave like any other
11653 * class in this regard by creating peer MediumAttachment
11654 * objects for session machines and share the data with the peer
11655 * machine.
11656 */
11657 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11658 it != mMediaData->mAttachments.end();
11659 ++it)
11660 {
11661 (*it)->updateParentMachine(mPeer);
11662 }
11663
11664 /* attach new data to the primary machine and reshare it */
11665 mPeer->mMediaData.attach(mMediaData);
11666 }
11667
11668 return;
11669}
11670
11671/**
11672 * Perform deferred deletion of implicitly created diffs.
11673 *
11674 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11675 * backed up).
11676 *
11677 * @note Locks this object for writing!
11678 */
11679void Machine::rollbackMedia()
11680{
11681 AutoCaller autoCaller(this);
11682 AssertComRCReturnVoid(autoCaller.rc());
11683
11684 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11685 LogFlowThisFunc(("Entering rollbackMedia\n"));
11686
11687 HRESULT rc = S_OK;
11688
11689 /* no attach/detach operations -- nothing to do */
11690 if (!mMediaData.isBackedUp())
11691 return;
11692
11693 /* enumerate new attachments */
11694 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11695 it != mMediaData->mAttachments.end();
11696 ++it)
11697 {
11698 MediumAttachment *pAttach = *it;
11699 /* Fix up the backrefs for DVD/floppy media. */
11700 if (pAttach->getType() != DeviceType_HardDisk)
11701 {
11702 Medium* pMedium = pAttach->getMedium();
11703 if (pMedium)
11704 {
11705 rc = pMedium->removeBackReference(mData->mUuid);
11706 AssertComRC(rc);
11707 }
11708 }
11709
11710 (*it)->rollback();
11711
11712 pAttach = *it;
11713 /* Fix up the backrefs for DVD/floppy media. */
11714 if (pAttach->getType() != DeviceType_HardDisk)
11715 {
11716 Medium* pMedium = pAttach->getMedium();
11717 if (pMedium)
11718 {
11719 rc = pMedium->addBackReference(mData->mUuid);
11720 AssertComRC(rc);
11721 }
11722 }
11723 }
11724
11725 /** @todo convert all this Machine-based voodoo to MediumAttachment
11726 * based rollback logic. */
11727 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11728
11729 return;
11730}
11731
11732/**
11733 * Returns true if the settings file is located in the directory named exactly
11734 * as the machine; this means, among other things, that the machine directory
11735 * should be auto-renamed.
11736 *
11737 * @param aSettingsDir if not NULL, the full machine settings file directory
11738 * name will be assigned there.
11739 *
11740 * @note Doesn't lock anything.
11741 * @note Not thread safe (must be called from this object's lock).
11742 */
11743bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11744{
11745 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11746 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11747 if (aSettingsDir)
11748 *aSettingsDir = strMachineDirName;
11749 strMachineDirName.stripPath(); // vmname
11750 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11751 strConfigFileOnly.stripPath() // vmname.vbox
11752 .stripExt(); // vmname
11753 /** @todo hack, make somehow use of ComposeMachineFilename */
11754 if (mUserData->s.fDirectoryIncludesUUID)
11755 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11756
11757 AssertReturn(!strMachineDirName.isEmpty(), false);
11758 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11759
11760 return strMachineDirName == strConfigFileOnly;
11761}
11762
11763/**
11764 * Discards all changes to machine settings.
11765 *
11766 * @param aNotify Whether to notify the direct session about changes or not.
11767 *
11768 * @note Locks objects for writing!
11769 */
11770void Machine::rollback(bool aNotify)
11771{
11772 AutoCaller autoCaller(this);
11773 AssertComRCReturn(autoCaller.rc(), (void)0);
11774
11775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11776
11777 if (!mStorageControllers.isNull())
11778 {
11779 if (mStorageControllers.isBackedUp())
11780 {
11781 /* unitialize all new devices (absent in the backed up list). */
11782 StorageControllerList::const_iterator it = mStorageControllers->begin();
11783 StorageControllerList *backedList = mStorageControllers.backedUpData();
11784 while (it != mStorageControllers->end())
11785 {
11786 if ( std::find(backedList->begin(), backedList->end(), *it)
11787 == backedList->end()
11788 )
11789 {
11790 (*it)->uninit();
11791 }
11792 ++it;
11793 }
11794
11795 /* restore the list */
11796 mStorageControllers.rollback();
11797 }
11798
11799 /* rollback any changes to devices after restoring the list */
11800 if (mData->flModifications & IsModified_Storage)
11801 {
11802 StorageControllerList::const_iterator it = mStorageControllers->begin();
11803 while (it != mStorageControllers->end())
11804 {
11805 (*it)->rollback();
11806 ++it;
11807 }
11808 }
11809 }
11810
11811 mUserData.rollback();
11812
11813 mHWData.rollback();
11814
11815 if (mData->flModifications & IsModified_Storage)
11816 rollbackMedia();
11817
11818 if (mBIOSSettings)
11819 mBIOSSettings->rollback();
11820
11821 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11822 mVRDEServer->rollback();
11823
11824 if (mAudioAdapter)
11825 mAudioAdapter->rollback();
11826
11827 if (mUSBController && (mData->flModifications & IsModified_USB))
11828 mUSBController->rollback();
11829
11830 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11831 mUSBDeviceFilters->rollback();
11832
11833 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11834 mBandwidthControl->rollback();
11835
11836 if (!mHWData.isNull())
11837 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11838 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11839 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11840 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11841
11842 if (mData->flModifications & IsModified_NetworkAdapters)
11843 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11844 if ( mNetworkAdapters[slot]
11845 && mNetworkAdapters[slot]->isModified())
11846 {
11847 mNetworkAdapters[slot]->rollback();
11848 networkAdapters[slot] = mNetworkAdapters[slot];
11849 }
11850
11851 if (mData->flModifications & IsModified_SerialPorts)
11852 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11853 if ( mSerialPorts[slot]
11854 && mSerialPorts[slot]->isModified())
11855 {
11856 mSerialPorts[slot]->rollback();
11857 serialPorts[slot] = mSerialPorts[slot];
11858 }
11859
11860 if (mData->flModifications & IsModified_ParallelPorts)
11861 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11862 if ( mParallelPorts[slot]
11863 && mParallelPorts[slot]->isModified())
11864 {
11865 mParallelPorts[slot]->rollback();
11866 parallelPorts[slot] = mParallelPorts[slot];
11867 }
11868
11869 if (aNotify)
11870 {
11871 /* inform the direct session about changes */
11872
11873 ComObjPtr<Machine> that = this;
11874 uint32_t flModifications = mData->flModifications;
11875 alock.release();
11876
11877 if (flModifications & IsModified_SharedFolders)
11878 that->onSharedFolderChange();
11879
11880 if (flModifications & IsModified_VRDEServer)
11881 that->onVRDEServerChange(/* aRestart */ TRUE);
11882 if (flModifications & IsModified_USB)
11883 that->onUSBControllerChange();
11884
11885 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11886 if (networkAdapters[slot])
11887 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11888 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11889 if (serialPorts[slot])
11890 that->onSerialPortChange(serialPorts[slot]);
11891 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11892 if (parallelPorts[slot])
11893 that->onParallelPortChange(parallelPorts[slot]);
11894
11895 if (flModifications & IsModified_Storage)
11896 that->onStorageControllerChange();
11897
11898#if 0
11899 if (flModifications & IsModified_BandwidthControl)
11900 that->onBandwidthControlChange();
11901#endif
11902 }
11903}
11904
11905/**
11906 * Commits all the changes to machine settings.
11907 *
11908 * Note that this operation is supposed to never fail.
11909 *
11910 * @note Locks this object and children for writing.
11911 */
11912void Machine::commit()
11913{
11914 AutoCaller autoCaller(this);
11915 AssertComRCReturnVoid(autoCaller.rc());
11916
11917 AutoCaller peerCaller(mPeer);
11918 AssertComRCReturnVoid(peerCaller.rc());
11919
11920 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11921
11922 /*
11923 * use safe commit to ensure Snapshot machines (that share mUserData)
11924 * will still refer to a valid memory location
11925 */
11926 mUserData.commitCopy();
11927
11928 mHWData.commit();
11929
11930 if (mMediaData.isBackedUp())
11931 commitMedia(Global::IsOnline(mData->mMachineState));
11932
11933 mBIOSSettings->commit();
11934 mVRDEServer->commit();
11935 mAudioAdapter->commit();
11936 mUSBController->commit();
11937 mUSBDeviceFilters->commit();
11938 mBandwidthControl->commit();
11939
11940 /* Since mNetworkAdapters is a list which might have been changed (resized)
11941 * without using the Backupable<> template we need to handle the copying
11942 * of the list entries manually, including the creation of peers for the
11943 * new objects. */
11944 bool commitNetworkAdapters = false;
11945 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11946 if (mPeer)
11947 {
11948 /* commit everything, even the ones which will go away */
11949 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11950 mNetworkAdapters[slot]->commit();
11951 /* copy over the new entries, creating a peer and uninit the original */
11952 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11953 for (size_t slot = 0; slot < newSize; slot++)
11954 {
11955 /* look if this adapter has a peer device */
11956 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11957 if (!peer)
11958 {
11959 /* no peer means the adapter is a newly created one;
11960 * create a peer owning data this data share it with */
11961 peer.createObject();
11962 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11963 }
11964 mPeer->mNetworkAdapters[slot] = peer;
11965 }
11966 /* uninit any no longer needed network adapters */
11967 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11968 mNetworkAdapters[slot]->uninit();
11969 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11970 {
11971 if (mPeer->mNetworkAdapters[slot])
11972 mPeer->mNetworkAdapters[slot]->uninit();
11973 }
11974 /* Keep the original network adapter count until this point, so that
11975 * discarding a chipset type change will not lose settings. */
11976 mNetworkAdapters.resize(newSize);
11977 mPeer->mNetworkAdapters.resize(newSize);
11978 }
11979 else
11980 {
11981 /* we have no peer (our parent is the newly created machine);
11982 * just commit changes to the network adapters */
11983 commitNetworkAdapters = true;
11984 }
11985 if (commitNetworkAdapters)
11986 {
11987 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11988 mNetworkAdapters[slot]->commit();
11989 }
11990
11991 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11992 mSerialPorts[slot]->commit();
11993 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11994 mParallelPorts[slot]->commit();
11995
11996 bool commitStorageControllers = false;
11997
11998 if (mStorageControllers.isBackedUp())
11999 {
12000 mStorageControllers.commit();
12001
12002 if (mPeer)
12003 {
12004 /* Commit all changes to new controllers (this will reshare data with
12005 * peers for those who have peers) */
12006 StorageControllerList *newList = new StorageControllerList();
12007 StorageControllerList::const_iterator it = mStorageControllers->begin();
12008 while (it != mStorageControllers->end())
12009 {
12010 (*it)->commit();
12011
12012 /* look if this controller has a peer device */
12013 ComObjPtr<StorageController> peer = (*it)->getPeer();
12014 if (!peer)
12015 {
12016 /* no peer means the device is a newly created one;
12017 * create a peer owning data this device share it with */
12018 peer.createObject();
12019 peer->init(mPeer, *it, true /* aReshare */);
12020 }
12021 else
12022 {
12023 /* remove peer from the old list */
12024 mPeer->mStorageControllers->remove(peer);
12025 }
12026 /* and add it to the new list */
12027 newList->push_back(peer);
12028
12029 ++it;
12030 }
12031
12032 /* uninit old peer's controllers that are left */
12033 it = mPeer->mStorageControllers->begin();
12034 while (it != mPeer->mStorageControllers->end())
12035 {
12036 (*it)->uninit();
12037 ++it;
12038 }
12039
12040 /* attach new list of controllers to our peer */
12041 mPeer->mStorageControllers.attach(newList);
12042 }
12043 else
12044 {
12045 /* we have no peer (our parent is the newly created machine);
12046 * just commit changes to devices */
12047 commitStorageControllers = true;
12048 }
12049 }
12050 else
12051 {
12052 /* the list of controllers itself is not changed,
12053 * just commit changes to controllers themselves */
12054 commitStorageControllers = true;
12055 }
12056
12057 if (commitStorageControllers)
12058 {
12059 StorageControllerList::const_iterator it = mStorageControllers->begin();
12060 while (it != mStorageControllers->end())
12061 {
12062 (*it)->commit();
12063 ++it;
12064 }
12065 }
12066
12067 if (isSessionMachine())
12068 {
12069 /* attach new data to the primary machine and reshare it */
12070 mPeer->mUserData.attach(mUserData);
12071 mPeer->mHWData.attach(mHWData);
12072 /* mMediaData is reshared by fixupMedia */
12073 // mPeer->mMediaData.attach(mMediaData);
12074 Assert(mPeer->mMediaData.data() == mMediaData.data());
12075 }
12076}
12077
12078/**
12079 * Copies all the hardware data from the given machine.
12080 *
12081 * Currently, only called when the VM is being restored from a snapshot. In
12082 * particular, this implies that the VM is not running during this method's
12083 * call.
12084 *
12085 * @note This method must be called from under this object's lock.
12086 *
12087 * @note This method doesn't call #commit(), so all data remains backed up and
12088 * unsaved.
12089 */
12090void Machine::copyFrom(Machine *aThat)
12091{
12092 AssertReturnVoid(!isSnapshotMachine());
12093 AssertReturnVoid(aThat->isSnapshotMachine());
12094
12095 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12096
12097 mHWData.assignCopy(aThat->mHWData);
12098
12099 // create copies of all shared folders (mHWData after attaching a copy
12100 // contains just references to original objects)
12101 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12102 it != mHWData->mSharedFolders.end();
12103 ++it)
12104 {
12105 ComObjPtr<SharedFolder> folder;
12106 folder.createObject();
12107 HRESULT rc = folder->initCopy(getMachine(), *it);
12108 AssertComRC(rc);
12109 *it = folder;
12110 }
12111
12112 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
12113 mVRDEServer->copyFrom(aThat->mVRDEServer);
12114 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
12115 mUSBController->copyFrom(aThat->mUSBController);
12116 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters);
12117 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
12118
12119 /* create private copies of all controllers */
12120 mStorageControllers.backup();
12121 mStorageControllers->clear();
12122 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12123 it != aThat->mStorageControllers->end();
12124 ++it)
12125 {
12126 ComObjPtr<StorageController> ctrl;
12127 ctrl.createObject();
12128 ctrl->initCopy(this, *it);
12129 mStorageControllers->push_back(ctrl);
12130 }
12131
12132 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12133 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12134 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
12135 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12136 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
12137 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12138 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
12139}
12140
12141/**
12142 * Returns whether the given storage controller is hotplug capable.
12143 *
12144 * @returns true if the controller supports hotplugging
12145 * false otherwise.
12146 * @param enmCtrlType The controller type to check for.
12147 */
12148bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12149{
12150 switch (enmCtrlType)
12151 {
12152 case StorageControllerType_IntelAhci:
12153 return true;
12154 case StorageControllerType_LsiLogic:
12155 case StorageControllerType_LsiLogicSas:
12156 case StorageControllerType_BusLogic:
12157 case StorageControllerType_PIIX3:
12158 case StorageControllerType_PIIX4:
12159 case StorageControllerType_ICH6:
12160 case StorageControllerType_I82078:
12161 default:
12162 return false;
12163 }
12164}
12165
12166#ifdef VBOX_WITH_RESOURCE_USAGE_API
12167
12168void Machine::getDiskList(MediaList &list)
12169{
12170 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12171 it != mMediaData->mAttachments.end();
12172 ++it)
12173 {
12174 MediumAttachment* pAttach = *it;
12175 /* just in case */
12176 AssertStmt(pAttach, continue);
12177
12178 AutoCaller localAutoCallerA(pAttach);
12179 if (FAILED(localAutoCallerA.rc())) continue;
12180
12181 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12182
12183 if (pAttach->getType() == DeviceType_HardDisk)
12184 list.push_back(pAttach->getMedium());
12185 }
12186}
12187
12188void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12189{
12190 AssertReturnVoid(isWriteLockOnCurrentThread());
12191 AssertPtrReturnVoid(aCollector);
12192
12193 pm::CollectorHAL *hal = aCollector->getHAL();
12194 /* Create sub metrics */
12195 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12196 "Percentage of processor time spent in user mode by the VM process.");
12197 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12198 "Percentage of processor time spent in kernel mode by the VM process.");
12199 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12200 "Size of resident portion of VM process in memory.");
12201 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12202 "Actual size of all VM disks combined.");
12203 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12204 "Network receive rate.");
12205 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12206 "Network transmit rate.");
12207 /* Create and register base metrics */
12208 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12209 cpuLoadUser, cpuLoadKernel);
12210 aCollector->registerBaseMetric(cpuLoad);
12211 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12212 ramUsageUsed);
12213 aCollector->registerBaseMetric(ramUsage);
12214 MediaList disks;
12215 getDiskList(disks);
12216 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12217 diskUsageUsed);
12218 aCollector->registerBaseMetric(diskUsage);
12219
12220 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12221 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12222 new pm::AggregateAvg()));
12223 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12224 new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12226 new pm::AggregateMax()));
12227 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12228 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12229 new pm::AggregateAvg()));
12230 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12231 new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12233 new pm::AggregateMax()));
12234
12235 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12236 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12237 new pm::AggregateAvg()));
12238 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12239 new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12241 new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12244 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12245 new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12247 new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12249 new pm::AggregateMax()));
12250
12251
12252 /* Guest metrics collector */
12253 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12254 aCollector->registerGuest(mCollectorGuest);
12255 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12256 this, __PRETTY_FUNCTION__, mCollectorGuest));
12257
12258 /* Create sub metrics */
12259 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12260 "Percentage of processor time spent in user mode as seen by the guest.");
12261 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12262 "Percentage of processor time spent in kernel mode as seen by the guest.");
12263 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12264 "Percentage of processor time spent idling as seen by the guest.");
12265
12266 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12267 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12268 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12269 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12270 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12271 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12272
12273 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12274
12275 /* Create and register base metrics */
12276 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12277 machineNetRx, machineNetTx);
12278 aCollector->registerBaseMetric(machineNetRate);
12279
12280 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12281 guestLoadUser, guestLoadKernel, guestLoadIdle);
12282 aCollector->registerBaseMetric(guestCpuLoad);
12283
12284 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12285 guestMemTotal, guestMemFree,
12286 guestMemBalloon, guestMemShared,
12287 guestMemCache, guestPagedTotal);
12288 aCollector->registerBaseMetric(guestCpuMem);
12289
12290 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12291 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12294
12295 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12296 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12297 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12298 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12301 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12302 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12304
12305 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12306 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12307 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12308 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12309
12310 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12311 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12312 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12314
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12316 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12317 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12318 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12319
12320 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12321 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12322 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12323 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12324
12325 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12326 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12327 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12328 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12329
12330 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12331 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12332 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12333 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12334
12335 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12336 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12337 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12338 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12339
12340 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12341 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12342 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12343 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12344}
12345
12346void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12347{
12348 AssertReturnVoid(isWriteLockOnCurrentThread());
12349
12350 if (aCollector)
12351 {
12352 aCollector->unregisterMetricsFor(aMachine);
12353 aCollector->unregisterBaseMetricsFor(aMachine);
12354 }
12355}
12356
12357#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12358
12359
12360////////////////////////////////////////////////////////////////////////////////
12361
12362DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12363
12364HRESULT SessionMachine::FinalConstruct()
12365{
12366 LogFlowThisFunc(("\n"));
12367
12368#if defined(RT_OS_WINDOWS)
12369 mIPCSem = NULL;
12370#elif defined(RT_OS_OS2)
12371 mIPCSem = NULLHANDLE;
12372#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12373 mIPCSem = -1;
12374#else
12375# error "Port me!"
12376#endif
12377
12378 return BaseFinalConstruct();
12379}
12380
12381void SessionMachine::FinalRelease()
12382{
12383 LogFlowThisFunc(("\n"));
12384
12385 uninit(Uninit::Unexpected);
12386
12387 BaseFinalRelease();
12388}
12389
12390/**
12391 * @note Must be called only by Machine::openSession() from its own write lock.
12392 */
12393HRESULT SessionMachine::init(Machine *aMachine)
12394{
12395 LogFlowThisFuncEnter();
12396 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12397
12398 AssertReturn(aMachine, E_INVALIDARG);
12399
12400 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12401
12402 /* Enclose the state transition NotReady->InInit->Ready */
12403 AutoInitSpan autoInitSpan(this);
12404 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12405
12406 /* create the interprocess semaphore */
12407#if defined(RT_OS_WINDOWS)
12408 mIPCSemName = aMachine->mData->m_strConfigFileFull;
12409 for (size_t i = 0; i < mIPCSemName.length(); i++)
12410 if (mIPCSemName.raw()[i] == '\\')
12411 mIPCSemName.raw()[i] = '/';
12412 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
12413 ComAssertMsgRet(mIPCSem,
12414 ("Cannot create IPC mutex '%ls', err=%d",
12415 mIPCSemName.raw(), ::GetLastError()),
12416 E_FAIL);
12417#elif defined(RT_OS_OS2)
12418 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
12419 aMachine->mData->mUuid.raw());
12420 mIPCSemName = ipcSem;
12421 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12422 ComAssertMsgRet(arc == NO_ERROR,
12423 ("Cannot create IPC mutex '%s', arc=%ld",
12424 ipcSem.c_str(), arc),
12425 E_FAIL);
12426#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12427# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12428# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12429 /** @todo Check that this still works correctly. */
12430 AssertCompileSize(key_t, 8);
12431# else
12432 AssertCompileSize(key_t, 4);
12433# endif
12434 key_t key;
12435 mIPCSem = -1;
12436 mIPCKey = "0";
12437 for (uint32_t i = 0; i < 1 << 24; i++)
12438 {
12439 key = ((uint32_t)'V' << 24) | i;
12440 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12441 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12442 {
12443 mIPCSem = sem;
12444 if (sem >= 0)
12445 mIPCKey = BstrFmt("%u", key);
12446 break;
12447 }
12448 }
12449# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12450 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12451 char *pszSemName = NULL;
12452 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12453 key_t key = ::ftok(pszSemName, 'V');
12454 RTStrFree(pszSemName);
12455
12456 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12457# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12458
12459 int errnoSave = errno;
12460 if (mIPCSem < 0 && errnoSave == ENOSYS)
12461 {
12462 setError(E_FAIL,
12463 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12464 "support for SysV IPC. Check the host kernel configuration for "
12465 "CONFIG_SYSVIPC=y"));
12466 return E_FAIL;
12467 }
12468 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12469 * the IPC semaphores */
12470 if (mIPCSem < 0 && errnoSave == ENOSPC)
12471 {
12472#ifdef RT_OS_LINUX
12473 setError(E_FAIL,
12474 tr("Cannot create IPC semaphore because the system limit for the "
12475 "maximum number of semaphore sets (SEMMNI), or the system wide "
12476 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12477 "current set of SysV IPC semaphores can be determined from "
12478 "the file /proc/sysvipc/sem"));
12479#else
12480 setError(E_FAIL,
12481 tr("Cannot create IPC semaphore because the system-imposed limit "
12482 "on the maximum number of allowed semaphores or semaphore "
12483 "identifiers system-wide would be exceeded"));
12484#endif
12485 return E_FAIL;
12486 }
12487 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12488 E_FAIL);
12489 /* set the initial value to 1 */
12490 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12491 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12492 E_FAIL);
12493#else
12494# error "Port me!"
12495#endif
12496
12497 /* memorize the peer Machine */
12498 unconst(mPeer) = aMachine;
12499 /* share the parent pointer */
12500 unconst(mParent) = aMachine->mParent;
12501
12502 /* take the pointers to data to share */
12503 mData.share(aMachine->mData);
12504 mSSData.share(aMachine->mSSData);
12505
12506 mUserData.share(aMachine->mUserData);
12507 mHWData.share(aMachine->mHWData);
12508 mMediaData.share(aMachine->mMediaData);
12509
12510 mStorageControllers.allocate();
12511 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12512 it != aMachine->mStorageControllers->end();
12513 ++it)
12514 {
12515 ComObjPtr<StorageController> ctl;
12516 ctl.createObject();
12517 ctl->init(this, *it);
12518 mStorageControllers->push_back(ctl);
12519 }
12520
12521 unconst(mBIOSSettings).createObject();
12522 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12523 /* create another VRDEServer object that will be mutable */
12524 unconst(mVRDEServer).createObject();
12525 mVRDEServer->init(this, aMachine->mVRDEServer);
12526 /* create another audio adapter object that will be mutable */
12527 unconst(mAudioAdapter).createObject();
12528 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12529 /* create a list of serial ports that will be mutable */
12530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12531 {
12532 unconst(mSerialPorts[slot]).createObject();
12533 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12534 }
12535 /* create a list of parallel ports that will be mutable */
12536 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12537 {
12538 unconst(mParallelPorts[slot]).createObject();
12539 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12540 }
12541 /* create another USB controller object that will be mutable */
12542 unconst(mUSBController).createObject();
12543 mUSBController->init(this, aMachine->mUSBController);
12544
12545 /* create another USB device filters object that will be mutable */
12546 unconst(mUSBDeviceFilters).createObject();
12547 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12548
12549 /* create a list of network adapters that will be mutable */
12550 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12551 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12552 {
12553 unconst(mNetworkAdapters[slot]).createObject();
12554 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12555 }
12556
12557 /* create another bandwidth control object that will be mutable */
12558 unconst(mBandwidthControl).createObject();
12559 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12560
12561 /* default is to delete saved state on Saved -> PoweredOff transition */
12562 mRemoveSavedState = true;
12563
12564 /* Confirm a successful initialization when it's the case */
12565 autoInitSpan.setSucceeded();
12566
12567 LogFlowThisFuncLeave();
12568 return S_OK;
12569}
12570
12571/**
12572 * Uninitializes this session object. If the reason is other than
12573 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12574 *
12575 * @param aReason uninitialization reason
12576 *
12577 * @note Locks mParent + this object for writing.
12578 */
12579void SessionMachine::uninit(Uninit::Reason aReason)
12580{
12581 LogFlowThisFuncEnter();
12582 LogFlowThisFunc(("reason=%d\n", aReason));
12583
12584 /*
12585 * Strongly reference ourselves to prevent this object deletion after
12586 * mData->mSession.mMachine.setNull() below (which can release the last
12587 * reference and call the destructor). Important: this must be done before
12588 * accessing any members (and before AutoUninitSpan that does it as well).
12589 * This self reference will be released as the very last step on return.
12590 */
12591 ComObjPtr<SessionMachine> selfRef = this;
12592
12593 /* Enclose the state transition Ready->InUninit->NotReady */
12594 AutoUninitSpan autoUninitSpan(this);
12595 if (autoUninitSpan.uninitDone())
12596 {
12597 LogFlowThisFunc(("Already uninitialized\n"));
12598 LogFlowThisFuncLeave();
12599 return;
12600 }
12601
12602 if (autoUninitSpan.initFailed())
12603 {
12604 /* We've been called by init() because it's failed. It's not really
12605 * necessary (nor it's safe) to perform the regular uninit sequence
12606 * below, the following is enough.
12607 */
12608 LogFlowThisFunc(("Initialization failed.\n"));
12609#if defined(RT_OS_WINDOWS)
12610 if (mIPCSem)
12611 ::CloseHandle(mIPCSem);
12612 mIPCSem = NULL;
12613#elif defined(RT_OS_OS2)
12614 if (mIPCSem != NULLHANDLE)
12615 ::DosCloseMutexSem(mIPCSem);
12616 mIPCSem = NULLHANDLE;
12617#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12618 if (mIPCSem >= 0)
12619 ::semctl(mIPCSem, 0, IPC_RMID);
12620 mIPCSem = -1;
12621# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12622 mIPCKey = "0";
12623# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12624#else
12625# error "Port me!"
12626#endif
12627 uninitDataAndChildObjects();
12628 mData.free();
12629 unconst(mParent) = NULL;
12630 unconst(mPeer) = NULL;
12631 LogFlowThisFuncLeave();
12632 return;
12633 }
12634
12635 MachineState_T lastState;
12636 {
12637 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12638 lastState = mData->mMachineState;
12639 }
12640 NOREF(lastState);
12641
12642#ifdef VBOX_WITH_USB
12643 // release all captured USB devices, but do this before requesting the locks below
12644 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12645 {
12646 /* Console::captureUSBDevices() is called in the VM process only after
12647 * setting the machine state to Starting or Restoring.
12648 * Console::detachAllUSBDevices() will be called upon successful
12649 * termination. So, we need to release USB devices only if there was
12650 * an abnormal termination of a running VM.
12651 *
12652 * This is identical to SessionMachine::DetachAllUSBDevices except
12653 * for the aAbnormal argument. */
12654 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
12655 AssertComRC(rc);
12656 NOREF(rc);
12657
12658 USBProxyService *service = mParent->host()->usbProxyService();
12659 if (service)
12660 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12661 }
12662#endif /* VBOX_WITH_USB */
12663
12664 // we need to lock this object in uninit() because the lock is shared
12665 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12666 // and others need mParent lock, and USB needs host lock.
12667 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12668
12669#ifdef VBOX_WITH_RESOURCE_USAGE_API
12670 /*
12671 * It is safe to call Machine::unregisterMetrics() here because
12672 * PerformanceCollector::samplerCallback no longer accesses guest methods
12673 * holding the lock.
12674 */
12675 unregisterMetrics(mParent->performanceCollector(), mPeer);
12676 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12677 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12678 this, __PRETTY_FUNCTION__, mCollectorGuest));
12679 if (mCollectorGuest)
12680 {
12681 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12682 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12683 mCollectorGuest = NULL;
12684 }
12685#endif
12686
12687 if (aReason == Uninit::Abnormal)
12688 {
12689 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12690 Global::IsOnlineOrTransient(lastState)));
12691
12692 /* reset the state to Aborted */
12693 if (mData->mMachineState != MachineState_Aborted)
12694 setMachineState(MachineState_Aborted);
12695 }
12696
12697 // any machine settings modified?
12698 if (mData->flModifications)
12699 {
12700 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12701 rollback(false /* aNotify */);
12702 }
12703
12704 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12705 || !mConsoleTaskData.mSnapshot);
12706 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12707 {
12708 LogWarningThisFunc(("canceling failed save state request!\n"));
12709 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12710 }
12711 else if (!mConsoleTaskData.mSnapshot.isNull())
12712 {
12713 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12714
12715 /* delete all differencing hard disks created (this will also attach
12716 * their parents back by rolling back mMediaData) */
12717 rollbackMedia();
12718
12719 // delete the saved state file (it might have been already created)
12720 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12721 // think it's still in use
12722 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12723 mConsoleTaskData.mSnapshot->uninit();
12724 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12725 }
12726
12727 if (!mData->mSession.mType.isEmpty())
12728 {
12729 /* mType is not null when this machine's process has been started by
12730 * Machine::LaunchVMProcess(), therefore it is our child. We
12731 * need to queue the PID to reap the process (and avoid zombies on
12732 * Linux). */
12733 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12734 mParent->addProcessToReap(mData->mSession.mPID);
12735 }
12736
12737 mData->mSession.mPID = NIL_RTPROCESS;
12738
12739 if (aReason == Uninit::Unexpected)
12740 {
12741 /* Uninitialization didn't come from #checkForDeath(), so tell the
12742 * client watcher thread to update the set of machines that have open
12743 * sessions. */
12744 mParent->updateClientWatcher();
12745 }
12746
12747 /* uninitialize all remote controls */
12748 if (mData->mSession.mRemoteControls.size())
12749 {
12750 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12751 mData->mSession.mRemoteControls.size()));
12752
12753 Data::Session::RemoteControlList::iterator it =
12754 mData->mSession.mRemoteControls.begin();
12755 while (it != mData->mSession.mRemoteControls.end())
12756 {
12757 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12758 HRESULT rc = (*it)->Uninitialize();
12759 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12760 if (FAILED(rc))
12761 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12762 ++it;
12763 }
12764 mData->mSession.mRemoteControls.clear();
12765 }
12766
12767 /*
12768 * An expected uninitialization can come only from #checkForDeath().
12769 * Otherwise it means that something's gone really wrong (for example,
12770 * the Session implementation has released the VirtualBox reference
12771 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12772 * etc). However, it's also possible, that the client releases the IPC
12773 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12774 * but the VirtualBox release event comes first to the server process.
12775 * This case is practically possible, so we should not assert on an
12776 * unexpected uninit, just log a warning.
12777 */
12778
12779 if ((aReason == Uninit::Unexpected))
12780 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12781
12782 if (aReason != Uninit::Normal)
12783 {
12784 mData->mSession.mDirectControl.setNull();
12785 }
12786 else
12787 {
12788 /* this must be null here (see #OnSessionEnd()) */
12789 Assert(mData->mSession.mDirectControl.isNull());
12790 Assert(mData->mSession.mState == SessionState_Unlocking);
12791 Assert(!mData->mSession.mProgress.isNull());
12792 }
12793 if (mData->mSession.mProgress)
12794 {
12795 if (aReason == Uninit::Normal)
12796 mData->mSession.mProgress->notifyComplete(S_OK);
12797 else
12798 mData->mSession.mProgress->notifyComplete(E_FAIL,
12799 COM_IIDOF(ISession),
12800 getComponentName(),
12801 tr("The VM session was aborted"));
12802 mData->mSession.mProgress.setNull();
12803 }
12804
12805 /* remove the association between the peer machine and this session machine */
12806 Assert( (SessionMachine*)mData->mSession.mMachine == this
12807 || aReason == Uninit::Unexpected);
12808
12809 /* reset the rest of session data */
12810 mData->mSession.mMachine.setNull();
12811 mData->mSession.mState = SessionState_Unlocked;
12812 mData->mSession.mType.setNull();
12813
12814 /* close the interprocess semaphore before leaving the exclusive lock */
12815#if defined(RT_OS_WINDOWS)
12816 if (mIPCSem)
12817 ::CloseHandle(mIPCSem);
12818 mIPCSem = NULL;
12819#elif defined(RT_OS_OS2)
12820 if (mIPCSem != NULLHANDLE)
12821 ::DosCloseMutexSem(mIPCSem);
12822 mIPCSem = NULLHANDLE;
12823#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12824 if (mIPCSem >= 0)
12825 ::semctl(mIPCSem, 0, IPC_RMID);
12826 mIPCSem = -1;
12827# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12828 mIPCKey = "0";
12829# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12830#else
12831# error "Port me!"
12832#endif
12833
12834 /* fire an event */
12835 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12836
12837 uninitDataAndChildObjects();
12838
12839 /* free the essential data structure last */
12840 mData.free();
12841
12842 /* release the exclusive lock before setting the below two to NULL */
12843 multilock.release();
12844
12845 unconst(mParent) = NULL;
12846 unconst(mPeer) = NULL;
12847
12848 LogFlowThisFuncLeave();
12849}
12850
12851// util::Lockable interface
12852////////////////////////////////////////////////////////////////////////////////
12853
12854/**
12855 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12856 * with the primary Machine instance (mPeer).
12857 */
12858RWLockHandle *SessionMachine::lockHandle() const
12859{
12860 AssertReturn(mPeer != NULL, NULL);
12861 return mPeer->lockHandle();
12862}
12863
12864// IInternalMachineControl methods
12865////////////////////////////////////////////////////////////////////////////////
12866
12867/**
12868 * Passes collected guest statistics to performance collector object
12869 */
12870STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12871 ULONG aCpuKernel, ULONG aCpuIdle,
12872 ULONG aMemTotal, ULONG aMemFree,
12873 ULONG aMemBalloon, ULONG aMemShared,
12874 ULONG aMemCache, ULONG aPageTotal,
12875 ULONG aAllocVMM, ULONG aFreeVMM,
12876 ULONG aBalloonedVMM, ULONG aSharedVMM,
12877 ULONG aVmNetRx, ULONG aVmNetTx)
12878{
12879#ifdef VBOX_WITH_RESOURCE_USAGE_API
12880 if (mCollectorGuest)
12881 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12882 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12883 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12884 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12885
12886 return S_OK;
12887#else
12888 NOREF(aValidStats);
12889 NOREF(aCpuUser);
12890 NOREF(aCpuKernel);
12891 NOREF(aCpuIdle);
12892 NOREF(aMemTotal);
12893 NOREF(aMemFree);
12894 NOREF(aMemBalloon);
12895 NOREF(aMemShared);
12896 NOREF(aMemCache);
12897 NOREF(aPageTotal);
12898 NOREF(aAllocVMM);
12899 NOREF(aFreeVMM);
12900 NOREF(aBalloonedVMM);
12901 NOREF(aSharedVMM);
12902 NOREF(aVmNetRx);
12903 NOREF(aVmNetTx);
12904 return E_NOTIMPL;
12905#endif
12906}
12907
12908/**
12909 * @note Locks this object for writing.
12910 */
12911STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12912{
12913 AutoCaller autoCaller(this);
12914 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12915
12916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12917
12918 mRemoveSavedState = aRemove;
12919
12920 return S_OK;
12921}
12922
12923/**
12924 * @note Locks the same as #setMachineState() does.
12925 */
12926STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12927{
12928 return setMachineState(aMachineState);
12929}
12930
12931/**
12932 * @note Locks this object for reading.
12933 */
12934STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12935{
12936 AutoCaller autoCaller(this);
12937 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12938
12939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12940
12941#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12942 mIPCSemName.cloneTo(aId);
12943 return S_OK;
12944#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12945# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12946 mIPCKey.cloneTo(aId);
12947# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12948 mData->m_strConfigFileFull.cloneTo(aId);
12949# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12950 return S_OK;
12951#else
12952# error "Port me!"
12953#endif
12954}
12955
12956/**
12957 * @note Locks this object for writing.
12958 */
12959STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12960{
12961 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12962 AutoCaller autoCaller(this);
12963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12964
12965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12966
12967 if (mData->mSession.mState != SessionState_Locked)
12968 return VBOX_E_INVALID_OBJECT_STATE;
12969
12970 if (!mData->mSession.mProgress.isNull())
12971 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12972
12973 LogFlowThisFunc(("returns S_OK.\n"));
12974 return S_OK;
12975}
12976
12977/**
12978 * @note Locks this object for writing.
12979 */
12980STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12981{
12982 AutoCaller autoCaller(this);
12983 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12984
12985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12986
12987 if (mData->mSession.mState != SessionState_Locked)
12988 return VBOX_E_INVALID_OBJECT_STATE;
12989
12990 /* Finalize the LaunchVMProcess progress object. */
12991 if (mData->mSession.mProgress)
12992 {
12993 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12994 mData->mSession.mProgress.setNull();
12995 }
12996
12997 if (SUCCEEDED((HRESULT)iResult))
12998 {
12999#ifdef VBOX_WITH_RESOURCE_USAGE_API
13000 /* The VM has been powered up successfully, so it makes sense
13001 * now to offer the performance metrics for a running machine
13002 * object. Doing it earlier wouldn't be safe. */
13003 registerMetrics(mParent->performanceCollector(), mPeer,
13004 mData->mSession.mPID);
13005#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13006 }
13007
13008 return S_OK;
13009}
13010
13011/**
13012 * @note Locks this object for writing.
13013 */
13014STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
13015{
13016 LogFlowThisFuncEnter();
13017
13018 CheckComArgOutPointerValid(aProgress);
13019
13020 AutoCaller autoCaller(this);
13021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13022
13023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13024
13025 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13026 E_FAIL);
13027
13028 /* create a progress object to track operation completion */
13029 ComObjPtr<Progress> pProgress;
13030 pProgress.createObject();
13031 pProgress->init(getVirtualBox(),
13032 static_cast<IMachine *>(this) /* aInitiator */,
13033 Bstr(tr("Stopping the virtual machine")).raw(),
13034 FALSE /* aCancelable */);
13035
13036 /* fill in the console task data */
13037 mConsoleTaskData.mLastState = mData->mMachineState;
13038 mConsoleTaskData.mProgress = pProgress;
13039
13040 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13041 setMachineState(MachineState_Stopping);
13042
13043 pProgress.queryInterfaceTo(aProgress);
13044
13045 return S_OK;
13046}
13047
13048/**
13049 * @note Locks this object for writing.
13050 */
13051STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
13052{
13053 LogFlowThisFuncEnter();
13054
13055 AutoCaller autoCaller(this);
13056 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13057
13058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13059
13060 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
13061 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
13062 && mConsoleTaskData.mLastState != MachineState_Null,
13063 E_FAIL);
13064
13065 /*
13066 * On failure, set the state to the state we had when BeginPoweringDown()
13067 * was called (this is expected by Console::PowerDown() and the associated
13068 * task). On success the VM process already changed the state to
13069 * MachineState_PoweredOff, so no need to do anything.
13070 */
13071 if (FAILED(iResult))
13072 setMachineState(mConsoleTaskData.mLastState);
13073
13074 /* notify the progress object about operation completion */
13075 Assert(mConsoleTaskData.mProgress);
13076 if (SUCCEEDED(iResult))
13077 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13078 else
13079 {
13080 Utf8Str strErrMsg(aErrMsg);
13081 if (strErrMsg.length())
13082 mConsoleTaskData.mProgress->notifyComplete(iResult,
13083 COM_IIDOF(ISession),
13084 getComponentName(),
13085 strErrMsg.c_str());
13086 else
13087 mConsoleTaskData.mProgress->notifyComplete(iResult);
13088 }
13089
13090 /* clear out the temporary saved state data */
13091 mConsoleTaskData.mLastState = MachineState_Null;
13092 mConsoleTaskData.mProgress.setNull();
13093
13094 LogFlowThisFuncLeave();
13095 return S_OK;
13096}
13097
13098
13099/**
13100 * Goes through the USB filters of the given machine to see if the given
13101 * device matches any filter or not.
13102 *
13103 * @note Locks the same as USBController::hasMatchingFilter() does.
13104 */
13105STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
13106 BOOL *aMatched,
13107 ULONG *aMaskedIfs)
13108{
13109 LogFlowThisFunc(("\n"));
13110
13111 CheckComArgNotNull(aUSBDevice);
13112 CheckComArgOutPointerValid(aMatched);
13113
13114 AutoCaller autoCaller(this);
13115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13116
13117#ifdef VBOX_WITH_USB
13118 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);
13119#else
13120 NOREF(aUSBDevice);
13121 NOREF(aMaskedIfs);
13122 *aMatched = FALSE;
13123#endif
13124
13125 return S_OK;
13126}
13127
13128/**
13129 * @note Locks the same as Host::captureUSBDevice() does.
13130 */
13131STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
13132{
13133 LogFlowThisFunc(("\n"));
13134
13135 AutoCaller autoCaller(this);
13136 AssertComRCReturnRC(autoCaller.rc());
13137
13138#ifdef VBOX_WITH_USB
13139 /* if captureDeviceForVM() fails, it must have set extended error info */
13140 clearError();
13141 MultiResult rc = mParent->host()->checkUSBProxyService();
13142 if (FAILED(rc)) return rc;
13143
13144 USBProxyService *service = mParent->host()->usbProxyService();
13145 AssertReturn(service, E_FAIL);
13146 return service->captureDeviceForVM(this, Guid(aId).ref());
13147#else
13148 NOREF(aId);
13149 return E_NOTIMPL;
13150#endif
13151}
13152
13153/**
13154 * @note Locks the same as Host::detachUSBDevice() does.
13155 */
13156STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
13157{
13158 LogFlowThisFunc(("\n"));
13159
13160 AutoCaller autoCaller(this);
13161 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13162
13163#ifdef VBOX_WITH_USB
13164 USBProxyService *service = mParent->host()->usbProxyService();
13165 AssertReturn(service, E_FAIL);
13166 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
13167#else
13168 NOREF(aId);
13169 NOREF(aDone);
13170 return E_NOTIMPL;
13171#endif
13172}
13173
13174/**
13175 * Inserts all machine filters to the USB proxy service and then calls
13176 * Host::autoCaptureUSBDevices().
13177 *
13178 * Called by Console from the VM process upon VM startup.
13179 *
13180 * @note Locks what called methods lock.
13181 */
13182STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
13183{
13184 LogFlowThisFunc(("\n"));
13185
13186 AutoCaller autoCaller(this);
13187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13188
13189#ifdef VBOX_WITH_USB
13190 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);
13191 AssertComRC(rc);
13192 NOREF(rc);
13193
13194 USBProxyService *service = mParent->host()->usbProxyService();
13195 AssertReturn(service, E_FAIL);
13196 return service->autoCaptureDevicesForVM(this);
13197#else
13198 return S_OK;
13199#endif
13200}
13201
13202/**
13203 * Removes all machine filters from the USB proxy service and then calls
13204 * Host::detachAllUSBDevices().
13205 *
13206 * Called by Console from the VM process upon normal VM termination or by
13207 * SessionMachine::uninit() upon abnormal VM termination (from under the
13208 * Machine/SessionMachine lock).
13209 *
13210 * @note Locks what called methods lock.
13211 */
13212STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
13213{
13214 LogFlowThisFunc(("\n"));
13215
13216 AutoCaller autoCaller(this);
13217 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13218
13219#ifdef VBOX_WITH_USB
13220 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);
13221 AssertComRC(rc);
13222 NOREF(rc);
13223
13224 USBProxyService *service = mParent->host()->usbProxyService();
13225 AssertReturn(service, E_FAIL);
13226 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13227#else
13228 NOREF(aDone);
13229 return S_OK;
13230#endif
13231}
13232
13233/**
13234 * @note Locks this object for writing.
13235 */
13236STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
13237 IProgress **aProgress)
13238{
13239 LogFlowThisFuncEnter();
13240
13241 AssertReturn(aSession, E_INVALIDARG);
13242 AssertReturn(aProgress, E_INVALIDARG);
13243
13244 AutoCaller autoCaller(this);
13245
13246 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
13247 /*
13248 * We don't assert below because it might happen that a non-direct session
13249 * informs us it is closed right after we've been uninitialized -- it's ok.
13250 */
13251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13252
13253 /* get IInternalSessionControl interface */
13254 ComPtr<IInternalSessionControl> control(aSession);
13255
13256 ComAssertRet(!control.isNull(), E_INVALIDARG);
13257
13258 /* Creating a Progress object requires the VirtualBox lock, and
13259 * thus locking it here is required by the lock order rules. */
13260 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13261
13262 if (control == mData->mSession.mDirectControl)
13263 {
13264 ComAssertRet(aProgress, E_POINTER);
13265
13266 /* The direct session is being normally closed by the client process
13267 * ----------------------------------------------------------------- */
13268
13269 /* go to the closing state (essential for all open*Session() calls and
13270 * for #checkForDeath()) */
13271 Assert(mData->mSession.mState == SessionState_Locked);
13272 mData->mSession.mState = SessionState_Unlocking;
13273
13274 /* set direct control to NULL to release the remote instance */
13275 mData->mSession.mDirectControl.setNull();
13276 LogFlowThisFunc(("Direct control is set to NULL\n"));
13277
13278 if (mData->mSession.mProgress)
13279 {
13280 /* finalize the progress, someone might wait if a frontend
13281 * closes the session before powering on the VM. */
13282 mData->mSession.mProgress->notifyComplete(E_FAIL,
13283 COM_IIDOF(ISession),
13284 getComponentName(),
13285 tr("The VM session was closed before any attempt to power it on"));
13286 mData->mSession.mProgress.setNull();
13287 }
13288
13289 /* Create the progress object the client will use to wait until
13290 * #checkForDeath() is called to uninitialize this session object after
13291 * it releases the IPC semaphore.
13292 * Note! Because we're "reusing" mProgress here, this must be a proxy
13293 * object just like for LaunchVMProcess. */
13294 Assert(mData->mSession.mProgress.isNull());
13295 ComObjPtr<ProgressProxy> progress;
13296 progress.createObject();
13297 ComPtr<IUnknown> pPeer(mPeer);
13298 progress->init(mParent, pPeer,
13299 Bstr(tr("Closing session")).raw(),
13300 FALSE /* aCancelable */);
13301 progress.queryInterfaceTo(aProgress);
13302 mData->mSession.mProgress = progress;
13303 }
13304 else
13305 {
13306 /* the remote session is being normally closed */
13307 Data::Session::RemoteControlList::iterator it =
13308 mData->mSession.mRemoteControls.begin();
13309 while (it != mData->mSession.mRemoteControls.end())
13310 {
13311 if (control == *it)
13312 break;
13313 ++it;
13314 }
13315 BOOL found = it != mData->mSession.mRemoteControls.end();
13316 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13317 E_INVALIDARG);
13318 // This MUST be erase(it), not remove(*it) as the latter triggers a
13319 // very nasty use after free due to the place where the value "lives".
13320 mData->mSession.mRemoteControls.erase(it);
13321 }
13322
13323 /* signal the client watcher thread, because the client is going away */
13324 mParent->updateClientWatcher();
13325
13326 LogFlowThisFuncLeave();
13327 return S_OK;
13328}
13329
13330/**
13331 * @note Locks this object for writing.
13332 */
13333STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
13334{
13335 LogFlowThisFuncEnter();
13336
13337 CheckComArgOutPointerValid(aProgress);
13338 CheckComArgOutPointerValid(aStateFilePath);
13339
13340 AutoCaller autoCaller(this);
13341 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13342
13343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13344
13345 AssertReturn( mData->mMachineState == MachineState_Paused
13346 && mConsoleTaskData.mLastState == MachineState_Null
13347 && mConsoleTaskData.strStateFilePath.isEmpty(),
13348 E_FAIL);
13349
13350 /* create a progress object to track operation completion */
13351 ComObjPtr<Progress> pProgress;
13352 pProgress.createObject();
13353 pProgress->init(getVirtualBox(),
13354 static_cast<IMachine *>(this) /* aInitiator */,
13355 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
13356 FALSE /* aCancelable */);
13357
13358 Utf8Str strStateFilePath;
13359 /* stateFilePath is null when the machine is not running */
13360 if (mData->mMachineState == MachineState_Paused)
13361 composeSavedStateFilename(strStateFilePath);
13362
13363 /* fill in the console task data */
13364 mConsoleTaskData.mLastState = mData->mMachineState;
13365 mConsoleTaskData.strStateFilePath = strStateFilePath;
13366 mConsoleTaskData.mProgress = pProgress;
13367
13368 /* set the state to Saving (this is expected by Console::SaveState()) */
13369 setMachineState(MachineState_Saving);
13370
13371 strStateFilePath.cloneTo(aStateFilePath);
13372 pProgress.queryInterfaceTo(aProgress);
13373
13374 return S_OK;
13375}
13376
13377/**
13378 * @note Locks mParent + this object for writing.
13379 */
13380STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
13381{
13382 LogFlowThisFunc(("\n"));
13383
13384 AutoCaller autoCaller(this);
13385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13386
13387 /* endSavingState() need mParent lock */
13388 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13389
13390 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
13391 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
13392 && mConsoleTaskData.mLastState != MachineState_Null
13393 && !mConsoleTaskData.strStateFilePath.isEmpty(),
13394 E_FAIL);
13395
13396 /*
13397 * On failure, set the state to the state we had when BeginSavingState()
13398 * was called (this is expected by Console::SaveState() and the associated
13399 * task). On success the VM process already changed the state to
13400 * MachineState_Saved, so no need to do anything.
13401 */
13402 if (FAILED(iResult))
13403 setMachineState(mConsoleTaskData.mLastState);
13404
13405 return endSavingState(iResult, aErrMsg);
13406}
13407
13408/**
13409 * @note Locks this object for writing.
13410 */
13411STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
13412{
13413 LogFlowThisFunc(("\n"));
13414
13415 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
13416
13417 AutoCaller autoCaller(this);
13418 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13419
13420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13421
13422 AssertReturn( mData->mMachineState == MachineState_PoweredOff
13423 || mData->mMachineState == MachineState_Teleported
13424 || mData->mMachineState == MachineState_Aborted
13425 , E_FAIL); /** @todo setError. */
13426
13427 Utf8Str stateFilePathFull = aSavedStateFile;
13428 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
13429 if (RT_FAILURE(vrc))
13430 return setError(VBOX_E_FILE_ERROR,
13431 tr("Invalid saved state file path '%ls' (%Rrc)"),
13432 aSavedStateFile,
13433 vrc);
13434
13435 mSSData->strStateFilePath = stateFilePathFull;
13436
13437 /* The below setMachineState() will detect the state transition and will
13438 * update the settings file */
13439
13440 return setMachineState(MachineState_Saved);
13441}
13442
13443STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13444 ComSafeArrayOut(BSTR, aValues),
13445 ComSafeArrayOut(LONG64, aTimestamps),
13446 ComSafeArrayOut(BSTR, aFlags))
13447{
13448 LogFlowThisFunc(("\n"));
13449
13450#ifdef VBOX_WITH_GUEST_PROPS
13451 using namespace guestProp;
13452
13453 AutoCaller autoCaller(this);
13454 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13455
13456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13457
13458 CheckComArgOutSafeArrayPointerValid(aNames);
13459 CheckComArgOutSafeArrayPointerValid(aValues);
13460 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13461 CheckComArgOutSafeArrayPointerValid(aFlags);
13462
13463 size_t cEntries = mHWData->mGuestProperties.size();
13464 com::SafeArray<BSTR> names(cEntries);
13465 com::SafeArray<BSTR> values(cEntries);
13466 com::SafeArray<LONG64> timestamps(cEntries);
13467 com::SafeArray<BSTR> flags(cEntries);
13468 unsigned i = 0;
13469 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13470 it != mHWData->mGuestProperties.end();
13471 ++it)
13472 {
13473 char szFlags[MAX_FLAGS_LEN + 1];
13474 it->first.cloneTo(&names[i]);
13475 it->second.strValue.cloneTo(&values[i]);
13476 timestamps[i] = it->second.mTimestamp;
13477 /* If it is NULL, keep it NULL. */
13478 if (it->second.mFlags)
13479 {
13480 writeFlags(it->second.mFlags, szFlags);
13481 Bstr(szFlags).cloneTo(&flags[i]);
13482 }
13483 else
13484 flags[i] = NULL;
13485 ++i;
13486 }
13487 names.detachTo(ComSafeArrayOutArg(aNames));
13488 values.detachTo(ComSafeArrayOutArg(aValues));
13489 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13490 flags.detachTo(ComSafeArrayOutArg(aFlags));
13491 return S_OK;
13492#else
13493 ReturnComNotImplemented();
13494#endif
13495}
13496
13497STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13498 IN_BSTR aValue,
13499 LONG64 aTimestamp,
13500 IN_BSTR aFlags)
13501{
13502 LogFlowThisFunc(("\n"));
13503
13504#ifdef VBOX_WITH_GUEST_PROPS
13505 using namespace guestProp;
13506
13507 CheckComArgStrNotEmptyOrNull(aName);
13508 CheckComArgNotNull(aValue);
13509 CheckComArgNotNull(aFlags);
13510
13511 try
13512 {
13513 /*
13514 * Convert input up front.
13515 */
13516 Utf8Str utf8Name(aName);
13517 uint32_t fFlags = NILFLAG;
13518 if (aFlags)
13519 {
13520 Utf8Str utf8Flags(aFlags);
13521 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13522 AssertRCReturn(vrc, E_INVALIDARG);
13523 }
13524
13525 /*
13526 * Now grab the object lock, validate the state and do the update.
13527 */
13528 AutoCaller autoCaller(this);
13529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13530
13531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13532
13533 switch (mData->mMachineState)
13534 {
13535 case MachineState_Paused:
13536 case MachineState_Running:
13537 case MachineState_Teleporting:
13538 case MachineState_TeleportingPausedVM:
13539 case MachineState_LiveSnapshotting:
13540 case MachineState_DeletingSnapshotOnline:
13541 case MachineState_DeletingSnapshotPaused:
13542 case MachineState_Saving:
13543 case MachineState_Stopping:
13544 break;
13545
13546 default:
13547 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13548 VBOX_E_INVALID_VM_STATE);
13549 }
13550
13551 setModified(IsModified_MachineData);
13552 mHWData.backup();
13553
13554 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';
13555 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13556 if (it != mHWData->mGuestProperties.end())
13557 {
13558 if (!fDelete)
13559 {
13560 it->second.strValue = aValue;
13561 it->second.mTimestamp = aTimestamp;
13562 it->second.mFlags = fFlags;
13563 }
13564 else
13565 mHWData->mGuestProperties.erase(it);
13566
13567 mData->mGuestPropertiesModified = TRUE;
13568 }
13569 else if (!fDelete)
13570 {
13571 HWData::GuestProperty prop;
13572 prop.strValue = aValue;
13573 prop.mTimestamp = aTimestamp;
13574 prop.mFlags = fFlags;
13575
13576 mHWData->mGuestProperties[utf8Name] = prop;
13577 mData->mGuestPropertiesModified = TRUE;
13578 }
13579
13580 /*
13581 * Send a callback notification if appropriate
13582 */
13583 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13584 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13585 RTSTR_MAX,
13586 utf8Name.c_str(),
13587 RTSTR_MAX, NULL)
13588 )
13589 {
13590 alock.release();
13591
13592 mParent->onGuestPropertyChange(mData->mUuid,
13593 aName,
13594 aValue,
13595 aFlags);
13596 }
13597 }
13598 catch (...)
13599 {
13600 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13601 }
13602 return S_OK;
13603#else
13604 ReturnComNotImplemented();
13605#endif
13606}
13607
13608STDMETHODIMP SessionMachine::LockMedia()
13609{
13610 AutoCaller autoCaller(this);
13611 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13612
13613 AutoMultiWriteLock2 alock(this->lockHandle(),
13614 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13615
13616 AssertReturn( mData->mMachineState == MachineState_Starting
13617 || mData->mMachineState == MachineState_Restoring
13618 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13619
13620 clearError();
13621 alock.release();
13622 return lockMedia();
13623}
13624
13625STDMETHODIMP SessionMachine::UnlockMedia()
13626{
13627 unlockMedia();
13628 return S_OK;
13629}
13630
13631STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13632 IMediumAttachment **aNewAttachment)
13633{
13634 CheckComArgNotNull(aAttachment);
13635 CheckComArgOutPointerValid(aNewAttachment);
13636
13637 AutoCaller autoCaller(this);
13638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13639
13640 // request the host lock first, since might be calling Host methods for getting host drives;
13641 // next, protect the media tree all the while we're in here, as well as our member variables
13642 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13643 this->lockHandle(),
13644 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13645
13646 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13647
13648 Bstr ctrlName;
13649 LONG lPort;
13650 LONG lDevice;
13651 bool fTempEject;
13652 {
13653 AutoCaller autoAttachCaller(this);
13654 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13655
13656 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13657
13658 /* Need to query the details first, as the IMediumAttachment reference
13659 * might be to the original settings, which we are going to change. */
13660 ctrlName = pAttach->getControllerName();
13661 lPort = pAttach->getPort();
13662 lDevice = pAttach->getDevice();
13663 fTempEject = pAttach->getTempEject();
13664 }
13665
13666 if (!fTempEject)
13667 {
13668 /* Remember previously mounted medium. The medium before taking the
13669 * backup is not necessarily the same thing. */
13670 ComObjPtr<Medium> oldmedium;
13671 oldmedium = pAttach->getMedium();
13672
13673 setModified(IsModified_Storage);
13674 mMediaData.backup();
13675
13676 // The backup operation makes the pAttach reference point to the
13677 // old settings. Re-get the correct reference.
13678 pAttach = findAttachment(mMediaData->mAttachments,
13679 ctrlName.raw(),
13680 lPort,
13681 lDevice);
13682
13683 {
13684 AutoCaller autoAttachCaller(this);
13685 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13686
13687 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13688 if (!oldmedium.isNull())
13689 oldmedium->removeBackReference(mData->mUuid);
13690
13691 pAttach->updateMedium(NULL);
13692 pAttach->updateEjected();
13693 }
13694
13695 setModified(IsModified_Storage);
13696 }
13697 else
13698 {
13699 {
13700 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13701 pAttach->updateEjected();
13702 }
13703 }
13704
13705 pAttach.queryInterfaceTo(aNewAttachment);
13706
13707 return S_OK;
13708}
13709
13710// public methods only for internal purposes
13711/////////////////////////////////////////////////////////////////////////////
13712
13713/**
13714 * Called from the client watcher thread to check for expected or unexpected
13715 * death of the client process that has a direct session to this machine.
13716 *
13717 * On Win32 and on OS/2, this method is called only when we've got the
13718 * mutex (i.e. the client has either died or terminated normally) so it always
13719 * returns @c true (the client is terminated, the session machine is
13720 * uninitialized).
13721 *
13722 * On other platforms, the method returns @c true if the client process has
13723 * terminated normally or abnormally and the session machine was uninitialized,
13724 * and @c false if the client process is still alive.
13725 *
13726 * @note Locks this object for writing.
13727 */
13728bool SessionMachine::checkForDeath()
13729{
13730 Uninit::Reason reason;
13731 bool terminated = false;
13732
13733 /* Enclose autoCaller with a block because calling uninit() from under it
13734 * will deadlock. */
13735 {
13736 AutoCaller autoCaller(this);
13737 if (!autoCaller.isOk())
13738 {
13739 /* return true if not ready, to cause the client watcher to exclude
13740 * the corresponding session from watching */
13741 LogFlowThisFunc(("Already uninitialized!\n"));
13742 return true;
13743 }
13744
13745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13746
13747 /* Determine the reason of death: if the session state is Closing here,
13748 * everything is fine. Otherwise it means that the client did not call
13749 * OnSessionEnd() before it released the IPC semaphore. This may happen
13750 * either because the client process has abnormally terminated, or
13751 * because it simply forgot to call ISession::Close() before exiting. We
13752 * threat the latter also as an abnormal termination (see
13753 * Session::uninit() for details). */
13754 reason = mData->mSession.mState == SessionState_Unlocking ?
13755 Uninit::Normal :
13756 Uninit::Abnormal;
13757
13758#if defined(RT_OS_WINDOWS)
13759
13760 AssertMsg(mIPCSem, ("semaphore must be created"));
13761
13762 /* release the IPC mutex */
13763 ::ReleaseMutex(mIPCSem);
13764
13765 terminated = true;
13766
13767#elif defined(RT_OS_OS2)
13768
13769 AssertMsg(mIPCSem, ("semaphore must be created"));
13770
13771 /* release the IPC mutex */
13772 ::DosReleaseMutexSem(mIPCSem);
13773
13774 terminated = true;
13775
13776#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13777
13778 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13779
13780 int val = ::semctl(mIPCSem, 0, GETVAL);
13781 if (val > 0)
13782 {
13783 /* the semaphore is signaled, meaning the session is terminated */
13784 terminated = true;
13785 }
13786
13787#else
13788# error "Port me!"
13789#endif
13790
13791 } /* AutoCaller block */
13792
13793 if (terminated)
13794 uninit(reason);
13795
13796 return terminated;
13797}
13798
13799/**
13800 * @note Locks this object for reading.
13801 */
13802HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13803{
13804 LogFlowThisFunc(("\n"));
13805
13806 AutoCaller autoCaller(this);
13807 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13808
13809 ComPtr<IInternalSessionControl> directControl;
13810 {
13811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13812 directControl = mData->mSession.mDirectControl;
13813 }
13814
13815 /* ignore notifications sent after #OnSessionEnd() is called */
13816 if (!directControl)
13817 return S_OK;
13818
13819 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13820}
13821
13822/**
13823 * @note Locks this object for reading.
13824 */
13825HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13826 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13827{
13828 LogFlowThisFunc(("\n"));
13829
13830 AutoCaller autoCaller(this);
13831 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13832
13833 ComPtr<IInternalSessionControl> directControl;
13834 {
13835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13836 directControl = mData->mSession.mDirectControl;
13837 }
13838
13839 /* ignore notifications sent after #OnSessionEnd() is called */
13840 if (!directControl)
13841 return S_OK;
13842 /*
13843 * instead acting like callback we ask IVirtualBox deliver corresponding event
13844 */
13845
13846 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13847 return S_OK;
13848}
13849
13850/**
13851 * @note Locks this object for reading.
13852 */
13853HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13854{
13855 LogFlowThisFunc(("\n"));
13856
13857 AutoCaller autoCaller(this);
13858 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13859
13860 ComPtr<IInternalSessionControl> directControl;
13861 {
13862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13863 directControl = mData->mSession.mDirectControl;
13864 }
13865
13866 /* ignore notifications sent after #OnSessionEnd() is called */
13867 if (!directControl)
13868 return S_OK;
13869
13870 return directControl->OnSerialPortChange(serialPort);
13871}
13872
13873/**
13874 * @note Locks this object for reading.
13875 */
13876HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13877{
13878 LogFlowThisFunc(("\n"));
13879
13880 AutoCaller autoCaller(this);
13881 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13882
13883 ComPtr<IInternalSessionControl> directControl;
13884 {
13885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13886 directControl = mData->mSession.mDirectControl;
13887 }
13888
13889 /* ignore notifications sent after #OnSessionEnd() is called */
13890 if (!directControl)
13891 return S_OK;
13892
13893 return directControl->OnParallelPortChange(parallelPort);
13894}
13895
13896/**
13897 * @note Locks this object for reading.
13898 */
13899HRESULT SessionMachine::onStorageControllerChange()
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13905
13906 ComPtr<IInternalSessionControl> directControl;
13907 {
13908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13909 directControl = mData->mSession.mDirectControl;
13910 }
13911
13912 /* ignore notifications sent after #OnSessionEnd() is called */
13913 if (!directControl)
13914 return S_OK;
13915
13916 return directControl->OnStorageControllerChange();
13917}
13918
13919/**
13920 * @note Locks this object for reading.
13921 */
13922HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13923{
13924 LogFlowThisFunc(("\n"));
13925
13926 AutoCaller autoCaller(this);
13927 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13928
13929 ComPtr<IInternalSessionControl> directControl;
13930 {
13931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13932 directControl = mData->mSession.mDirectControl;
13933 }
13934
13935 /* ignore notifications sent after #OnSessionEnd() is called */
13936 if (!directControl)
13937 return S_OK;
13938
13939 return directControl->OnMediumChange(aAttachment, aForce);
13940}
13941
13942/**
13943 * @note Locks this object for reading.
13944 */
13945HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13951
13952 ComPtr<IInternalSessionControl> directControl;
13953 {
13954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13955 directControl = mData->mSession.mDirectControl;
13956 }
13957
13958 /* ignore notifications sent after #OnSessionEnd() is called */
13959 if (!directControl)
13960 return S_OK;
13961
13962 return directControl->OnCPUChange(aCPU, aRemove);
13963}
13964
13965HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13966{
13967 LogFlowThisFunc(("\n"));
13968
13969 AutoCaller autoCaller(this);
13970 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13971
13972 ComPtr<IInternalSessionControl> directControl;
13973 {
13974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13975 directControl = mData->mSession.mDirectControl;
13976 }
13977
13978 /* ignore notifications sent after #OnSessionEnd() is called */
13979 if (!directControl)
13980 return S_OK;
13981
13982 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13983}
13984
13985/**
13986 * @note Locks this object for reading.
13987 */
13988HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13989{
13990 LogFlowThisFunc(("\n"));
13991
13992 AutoCaller autoCaller(this);
13993 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13994
13995 ComPtr<IInternalSessionControl> directControl;
13996 {
13997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13998 directControl = mData->mSession.mDirectControl;
13999 }
14000
14001 /* ignore notifications sent after #OnSessionEnd() is called */
14002 if (!directControl)
14003 return S_OK;
14004
14005 return directControl->OnVRDEServerChange(aRestart);
14006}
14007
14008/**
14009 * @note Locks this object for reading.
14010 */
14011HRESULT SessionMachine::onVideoCaptureChange()
14012{
14013 LogFlowThisFunc(("\n"));
14014
14015 AutoCaller autoCaller(this);
14016 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14017
14018 ComPtr<IInternalSessionControl> directControl;
14019 {
14020 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14021 directControl = mData->mSession.mDirectControl;
14022 }
14023
14024 /* ignore notifications sent after #OnSessionEnd() is called */
14025 if (!directControl)
14026 return S_OK;
14027
14028 return directControl->OnVideoCaptureChange();
14029}
14030
14031/**
14032 * @note Locks this object for reading.
14033 */
14034HRESULT SessionMachine::onUSBControllerChange()
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 AutoCaller autoCaller(this);
14039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14040
14041 ComPtr<IInternalSessionControl> directControl;
14042 {
14043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14044 directControl = mData->mSession.mDirectControl;
14045 }
14046
14047 /* ignore notifications sent after #OnSessionEnd() is called */
14048 if (!directControl)
14049 return S_OK;
14050
14051 return directControl->OnUSBControllerChange();
14052}
14053
14054/**
14055 * @note Locks this object for reading.
14056 */
14057HRESULT SessionMachine::onSharedFolderChange()
14058{
14059 LogFlowThisFunc(("\n"));
14060
14061 AutoCaller autoCaller(this);
14062 AssertComRCReturnRC(autoCaller.rc());
14063
14064 ComPtr<IInternalSessionControl> directControl;
14065 {
14066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14067 directControl = mData->mSession.mDirectControl;
14068 }
14069
14070 /* ignore notifications sent after #OnSessionEnd() is called */
14071 if (!directControl)
14072 return S_OK;
14073
14074 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14075}
14076
14077/**
14078 * @note Locks this object for reading.
14079 */
14080HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
14081{
14082 LogFlowThisFunc(("\n"));
14083
14084 AutoCaller autoCaller(this);
14085 AssertComRCReturnRC(autoCaller.rc());
14086
14087 ComPtr<IInternalSessionControl> directControl;
14088 {
14089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14090 directControl = mData->mSession.mDirectControl;
14091 }
14092
14093 /* ignore notifications sent after #OnSessionEnd() is called */
14094 if (!directControl)
14095 return S_OK;
14096
14097 return directControl->OnClipboardModeChange(aClipboardMode);
14098}
14099
14100/**
14101 * @note Locks this object for reading.
14102 */
14103HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturnRC(autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 directControl = mData->mSession.mDirectControl;
14114 }
14115
14116 /* ignore notifications sent after #OnSessionEnd() is called */
14117 if (!directControl)
14118 return S_OK;
14119
14120 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
14121}
14122
14123/**
14124 * @note Locks this object for reading.
14125 */
14126HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14127{
14128 LogFlowThisFunc(("\n"));
14129
14130 AutoCaller autoCaller(this);
14131 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14132
14133 ComPtr<IInternalSessionControl> directControl;
14134 {
14135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14136 directControl = mData->mSession.mDirectControl;
14137 }
14138
14139 /* ignore notifications sent after #OnSessionEnd() is called */
14140 if (!directControl)
14141 return S_OK;
14142
14143 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14144}
14145
14146/**
14147 * @note Locks this object for reading.
14148 */
14149HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14150{
14151 LogFlowThisFunc(("\n"));
14152
14153 AutoCaller autoCaller(this);
14154 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14155
14156 ComPtr<IInternalSessionControl> directControl;
14157 {
14158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* ignore notifications sent after #OnSessionEnd() is called */
14163 if (!directControl)
14164 return S_OK;
14165
14166 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14167}
14168
14169/**
14170 * Returns @c true if this machine's USB controller reports it has a matching
14171 * filter for the given USB device and @c false otherwise.
14172 *
14173 * @note locks this object for reading.
14174 */
14175bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14176{
14177 AutoCaller autoCaller(this);
14178 /* silently return if not ready -- this method may be called after the
14179 * direct machine session has been called */
14180 if (!autoCaller.isOk())
14181 return false;
14182
14183#ifdef VBOX_WITH_USB
14184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14185
14186 switch (mData->mMachineState)
14187 {
14188 case MachineState_Starting:
14189 case MachineState_Restoring:
14190 case MachineState_TeleportingIn:
14191 case MachineState_Paused:
14192 case MachineState_Running:
14193 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14194 * elsewhere... */
14195 alock.release();
14196 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs);
14197 default: break;
14198 }
14199#else
14200 NOREF(aDevice);
14201 NOREF(aMaskedIfs);
14202#endif
14203 return false;
14204}
14205
14206/**
14207 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14208 */
14209HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
14210 IVirtualBoxErrorInfo *aError,
14211 ULONG aMaskedIfs)
14212{
14213 LogFlowThisFunc(("\n"));
14214
14215 AutoCaller autoCaller(this);
14216
14217 /* This notification may happen after the machine object has been
14218 * uninitialized (the session was closed), so don't assert. */
14219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14220
14221 ComPtr<IInternalSessionControl> directControl;
14222 {
14223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14224 directControl = mData->mSession.mDirectControl;
14225 }
14226
14227 /* fail on notifications sent after #OnSessionEnd() is called, it is
14228 * expected by the caller */
14229 if (!directControl)
14230 return E_FAIL;
14231
14232 /* No locks should be held at this point. */
14233 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14234 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14235
14236 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
14237}
14238
14239/**
14240 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14241 */
14242HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
14243 IVirtualBoxErrorInfo *aError)
14244{
14245 LogFlowThisFunc(("\n"));
14246
14247 AutoCaller autoCaller(this);
14248
14249 /* This notification may happen after the machine object has been
14250 * uninitialized (the session was closed), so don't assert. */
14251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14252
14253 ComPtr<IInternalSessionControl> directControl;
14254 {
14255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14256 directControl = mData->mSession.mDirectControl;
14257 }
14258
14259 /* fail on notifications sent after #OnSessionEnd() is called, it is
14260 * expected by the caller */
14261 if (!directControl)
14262 return E_FAIL;
14263
14264 /* No locks should be held at this point. */
14265 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14266 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14267
14268 return directControl->OnUSBDeviceDetach(aId, aError);
14269}
14270
14271// protected methods
14272/////////////////////////////////////////////////////////////////////////////
14273
14274/**
14275 * Helper method to finalize saving the state.
14276 *
14277 * @note Must be called from under this object's lock.
14278 *
14279 * @param aRc S_OK if the snapshot has been taken successfully
14280 * @param aErrMsg human readable error message for failure
14281 *
14282 * @note Locks mParent + this objects for writing.
14283 */
14284HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
14285{
14286 LogFlowThisFuncEnter();
14287
14288 AutoCaller autoCaller(this);
14289 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14290
14291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14292
14293 HRESULT rc = S_OK;
14294
14295 if (SUCCEEDED(aRc))
14296 {
14297 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
14298
14299 /* save all VM settings */
14300 rc = saveSettings(NULL);
14301 // no need to check whether VirtualBox.xml needs saving also since
14302 // we can't have a name change pending at this point
14303 }
14304 else
14305 {
14306 // delete the saved state file (it might have been already created);
14307 // we need not check whether this is shared with a snapshot here because
14308 // we certainly created this saved state file here anew
14309 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
14310 }
14311
14312 /* notify the progress object about operation completion */
14313 Assert(mConsoleTaskData.mProgress);
14314 if (SUCCEEDED(aRc))
14315 mConsoleTaskData.mProgress->notifyComplete(S_OK);
14316 else
14317 {
14318 if (aErrMsg.length())
14319 mConsoleTaskData.mProgress->notifyComplete(aRc,
14320 COM_IIDOF(ISession),
14321 getComponentName(),
14322 aErrMsg.c_str());
14323 else
14324 mConsoleTaskData.mProgress->notifyComplete(aRc);
14325 }
14326
14327 /* clear out the temporary saved state data */
14328 mConsoleTaskData.mLastState = MachineState_Null;
14329 mConsoleTaskData.strStateFilePath.setNull();
14330 mConsoleTaskData.mProgress.setNull();
14331
14332 LogFlowThisFuncLeave();
14333 return rc;
14334}
14335
14336/**
14337 * Deletes the given file if it is no longer in use by either the current machine state
14338 * (if the machine is "saved") or any of the machine's snapshots.
14339 *
14340 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14341 * but is different for each SnapshotMachine. When calling this, the order of calling this
14342 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14343 * is therefore critical. I know, it's all rather messy.
14344 *
14345 * @param strStateFile
14346 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
14347 */
14348void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
14349 Snapshot *pSnapshotToIgnore)
14350{
14351 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14352 if ( (strStateFile.isNotEmpty())
14353 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14354 )
14355 // ... and it must also not be shared with other snapshots
14356 if ( !mData->mFirstSnapshot
14357 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14358 // this checks the SnapshotMachine's state file paths
14359 )
14360 RTFileDelete(strStateFile.c_str());
14361}
14362
14363/**
14364 * Locks the attached media.
14365 *
14366 * All attached hard disks are locked for writing and DVD/floppy are locked for
14367 * reading. Parents of attached hard disks (if any) are locked for reading.
14368 *
14369 * This method also performs accessibility check of all media it locks: if some
14370 * media is inaccessible, the method will return a failure and a bunch of
14371 * extended error info objects per each inaccessible medium.
14372 *
14373 * Note that this method is atomic: if it returns a success, all media are
14374 * locked as described above; on failure no media is locked at all (all
14375 * succeeded individual locks will be undone).
14376 *
14377 * The caller is responsible for doing the necessary state sanity checks.
14378 *
14379 * The locks made by this method must be undone by calling #unlockMedia() when
14380 * no more needed.
14381 */
14382HRESULT SessionMachine::lockMedia()
14383{
14384 AutoCaller autoCaller(this);
14385 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14386
14387 AutoMultiWriteLock2 alock(this->lockHandle(),
14388 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14389
14390 /* bail out if trying to lock things with already set up locking */
14391 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14392
14393 MultiResult mrc(S_OK);
14394
14395 /* Collect locking information for all medium objects attached to the VM. */
14396 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14397 it != mMediaData->mAttachments.end();
14398 ++it)
14399 {
14400 MediumAttachment* pAtt = *it;
14401 DeviceType_T devType = pAtt->getType();
14402 Medium *pMedium = pAtt->getMedium();
14403
14404 MediumLockList *pMediumLockList(new MediumLockList());
14405 // There can be attachments without a medium (floppy/dvd), and thus
14406 // it's impossible to create a medium lock list. It still makes sense
14407 // to have the empty medium lock list in the map in case a medium is
14408 // attached later.
14409 if (pMedium != NULL)
14410 {
14411 MediumType_T mediumType = pMedium->getType();
14412 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14413 || mediumType == MediumType_Shareable;
14414 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14415
14416 alock.release();
14417 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14418 !fIsReadOnlyLock /* fMediumLockWrite */,
14419 NULL,
14420 *pMediumLockList);
14421 alock.acquire();
14422 if (FAILED(mrc))
14423 {
14424 delete pMediumLockList;
14425 mData->mSession.mLockedMedia.Clear();
14426 break;
14427 }
14428 }
14429
14430 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14431 if (FAILED(rc))
14432 {
14433 mData->mSession.mLockedMedia.Clear();
14434 mrc = setError(rc,
14435 tr("Collecting locking information for all attached media failed"));
14436 break;
14437 }
14438 }
14439
14440 if (SUCCEEDED(mrc))
14441 {
14442 /* Now lock all media. If this fails, nothing is locked. */
14443 alock.release();
14444 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14445 alock.acquire();
14446 if (FAILED(rc))
14447 {
14448 mrc = setError(rc,
14449 tr("Locking of attached media failed"));
14450 }
14451 }
14452
14453 return mrc;
14454}
14455
14456/**
14457 * Undoes the locks made by by #lockMedia().
14458 */
14459void SessionMachine::unlockMedia()
14460{
14461 AutoCaller autoCaller(this);
14462 AssertComRCReturnVoid(autoCaller.rc());
14463
14464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14465
14466 /* we may be holding important error info on the current thread;
14467 * preserve it */
14468 ErrorInfoKeeper eik;
14469
14470 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14471 AssertComRC(rc);
14472}
14473
14474/**
14475 * Helper to change the machine state (reimplementation).
14476 *
14477 * @note Locks this object for writing.
14478 * @note This method must not call saveSettings or SaveSettings, otherwise
14479 * it can cause crashes in random places due to unexpectedly committing
14480 * the current settings. The caller is responsible for that. The call
14481 * to saveStateSettings is fine, because this method does not commit.
14482 */
14483HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14484{
14485 LogFlowThisFuncEnter();
14486 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14487
14488 AutoCaller autoCaller(this);
14489 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14490
14491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14492
14493 MachineState_T oldMachineState = mData->mMachineState;
14494
14495 AssertMsgReturn(oldMachineState != aMachineState,
14496 ("oldMachineState=%s, aMachineState=%s\n",
14497 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14498 E_FAIL);
14499
14500 HRESULT rc = S_OK;
14501
14502 int stsFlags = 0;
14503 bool deleteSavedState = false;
14504
14505 /* detect some state transitions */
14506
14507 if ( ( oldMachineState == MachineState_Saved
14508 && aMachineState == MachineState_Restoring)
14509 || ( ( oldMachineState == MachineState_PoweredOff
14510 || oldMachineState == MachineState_Teleported
14511 || oldMachineState == MachineState_Aborted
14512 )
14513 && ( aMachineState == MachineState_TeleportingIn
14514 || aMachineState == MachineState_Starting
14515 )
14516 )
14517 )
14518 {
14519 /* The EMT thread is about to start */
14520
14521 /* Nothing to do here for now... */
14522
14523 /// @todo NEWMEDIA don't let mDVDDrive and other children
14524 /// change anything when in the Starting/Restoring state
14525 }
14526 else if ( ( oldMachineState == MachineState_Running
14527 || oldMachineState == MachineState_Paused
14528 || oldMachineState == MachineState_Teleporting
14529 || oldMachineState == MachineState_LiveSnapshotting
14530 || oldMachineState == MachineState_Stuck
14531 || oldMachineState == MachineState_Starting
14532 || oldMachineState == MachineState_Stopping
14533 || oldMachineState == MachineState_Saving
14534 || oldMachineState == MachineState_Restoring
14535 || oldMachineState == MachineState_TeleportingPausedVM
14536 || oldMachineState == MachineState_TeleportingIn
14537 )
14538 && ( aMachineState == MachineState_PoweredOff
14539 || aMachineState == MachineState_Saved
14540 || aMachineState == MachineState_Teleported
14541 || aMachineState == MachineState_Aborted
14542 )
14543 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14544 * snapshot */
14545 && ( mConsoleTaskData.mSnapshot.isNull()
14546 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14547 )
14548 )
14549 {
14550 /* The EMT thread has just stopped, unlock attached media. Note that as
14551 * opposed to locking that is done from Console, we do unlocking here
14552 * because the VM process may have aborted before having a chance to
14553 * properly unlock all media it locked. */
14554
14555 unlockMedia();
14556 }
14557
14558 if (oldMachineState == MachineState_Restoring)
14559 {
14560 if (aMachineState != MachineState_Saved)
14561 {
14562 /*
14563 * delete the saved state file once the machine has finished
14564 * restoring from it (note that Console sets the state from
14565 * Restoring to Saved if the VM couldn't restore successfully,
14566 * to give the user an ability to fix an error and retry --
14567 * we keep the saved state file in this case)
14568 */
14569 deleteSavedState = true;
14570 }
14571 }
14572 else if ( oldMachineState == MachineState_Saved
14573 && ( aMachineState == MachineState_PoweredOff
14574 || aMachineState == MachineState_Aborted
14575 || aMachineState == MachineState_Teleported
14576 )
14577 )
14578 {
14579 /*
14580 * delete the saved state after Console::ForgetSavedState() is called
14581 * or if the VM process (owning a direct VM session) crashed while the
14582 * VM was Saved
14583 */
14584
14585 /// @todo (dmik)
14586 // Not sure that deleting the saved state file just because of the
14587 // client death before it attempted to restore the VM is a good
14588 // thing. But when it crashes we need to go to the Aborted state
14589 // which cannot have the saved state file associated... The only
14590 // way to fix this is to make the Aborted condition not a VM state
14591 // but a bool flag: i.e., when a crash occurs, set it to true and
14592 // change the state to PoweredOff or Saved depending on the
14593 // saved state presence.
14594
14595 deleteSavedState = true;
14596 mData->mCurrentStateModified = TRUE;
14597 stsFlags |= SaveSTS_CurStateModified;
14598 }
14599
14600 if ( aMachineState == MachineState_Starting
14601 || aMachineState == MachineState_Restoring
14602 || aMachineState == MachineState_TeleportingIn
14603 )
14604 {
14605 /* set the current state modified flag to indicate that the current
14606 * state is no more identical to the state in the
14607 * current snapshot */
14608 if (!mData->mCurrentSnapshot.isNull())
14609 {
14610 mData->mCurrentStateModified = TRUE;
14611 stsFlags |= SaveSTS_CurStateModified;
14612 }
14613 }
14614
14615 if (deleteSavedState)
14616 {
14617 if (mRemoveSavedState)
14618 {
14619 Assert(!mSSData->strStateFilePath.isEmpty());
14620
14621 // it is safe to delete the saved state file if ...
14622 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14623 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14624 // ... none of the snapshots share the saved state file
14625 )
14626 RTFileDelete(mSSData->strStateFilePath.c_str());
14627 }
14628
14629 mSSData->strStateFilePath.setNull();
14630 stsFlags |= SaveSTS_StateFilePath;
14631 }
14632
14633 /* redirect to the underlying peer machine */
14634 mPeer->setMachineState(aMachineState);
14635
14636 if ( aMachineState == MachineState_PoweredOff
14637 || aMachineState == MachineState_Teleported
14638 || aMachineState == MachineState_Aborted
14639 || aMachineState == MachineState_Saved)
14640 {
14641 /* the machine has stopped execution
14642 * (or the saved state file was adopted) */
14643 stsFlags |= SaveSTS_StateTimeStamp;
14644 }
14645
14646 if ( ( oldMachineState == MachineState_PoweredOff
14647 || oldMachineState == MachineState_Aborted
14648 || oldMachineState == MachineState_Teleported
14649 )
14650 && aMachineState == MachineState_Saved)
14651 {
14652 /* the saved state file was adopted */
14653 Assert(!mSSData->strStateFilePath.isEmpty());
14654 stsFlags |= SaveSTS_StateFilePath;
14655 }
14656
14657#ifdef VBOX_WITH_GUEST_PROPS
14658 if ( aMachineState == MachineState_PoweredOff
14659 || aMachineState == MachineState_Aborted
14660 || aMachineState == MachineState_Teleported)
14661 {
14662 /* Make sure any transient guest properties get removed from the
14663 * property store on shutdown. */
14664
14665 HWData::GuestPropertyMap::const_iterator it;
14666 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14667 if (!fNeedsSaving)
14668 for (it = mHWData->mGuestProperties.begin();
14669 it != mHWData->mGuestProperties.end(); ++it)
14670 if ( (it->second.mFlags & guestProp::TRANSIENT)
14671 || (it->second.mFlags & guestProp::TRANSRESET))
14672 {
14673 fNeedsSaving = true;
14674 break;
14675 }
14676 if (fNeedsSaving)
14677 {
14678 mData->mCurrentStateModified = TRUE;
14679 stsFlags |= SaveSTS_CurStateModified;
14680 }
14681 }
14682#endif
14683
14684 rc = saveStateSettings(stsFlags);
14685
14686 if ( ( oldMachineState != MachineState_PoweredOff
14687 && oldMachineState != MachineState_Aborted
14688 && oldMachineState != MachineState_Teleported
14689 )
14690 && ( aMachineState == MachineState_PoweredOff
14691 || aMachineState == MachineState_Aborted
14692 || aMachineState == MachineState_Teleported
14693 )
14694 )
14695 {
14696 /* we've been shut down for any reason */
14697 /* no special action so far */
14698 }
14699
14700 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14701 LogFlowThisFuncLeave();
14702 return rc;
14703}
14704
14705/**
14706 * Sends the current machine state value to the VM process.
14707 *
14708 * @note Locks this object for reading, then calls a client process.
14709 */
14710HRESULT SessionMachine::updateMachineStateOnClient()
14711{
14712 AutoCaller autoCaller(this);
14713 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14714
14715 ComPtr<IInternalSessionControl> directControl;
14716 {
14717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14718 AssertReturn(!!mData, E_FAIL);
14719 directControl = mData->mSession.mDirectControl;
14720
14721 /* directControl may be already set to NULL here in #OnSessionEnd()
14722 * called too early by the direct session process while there is still
14723 * some operation (like deleting the snapshot) in progress. The client
14724 * process in this case is waiting inside Session::close() for the
14725 * "end session" process object to complete, while #uninit() called by
14726 * #checkForDeath() on the Watcher thread is waiting for the pending
14727 * operation to complete. For now, we accept this inconsistent behavior
14728 * and simply do nothing here. */
14729
14730 if (mData->mSession.mState == SessionState_Unlocking)
14731 return S_OK;
14732
14733 AssertReturn(!directControl.isNull(), E_FAIL);
14734 }
14735
14736 return directControl->UpdateMachineState(mData->mMachineState);
14737}
Note: See TracBrowser for help on using the repository browser.

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