VirtualBox

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

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

Main/Metics: VM disk usage metric (#6345)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 470.0 KB
Line 
1/* $Id: MachineImpl.cpp 43949 2012-11-23 13:43:19Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = true;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine()
247 : mCollectorGuest(NULL),
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->applyDefaults(aOsType);
352 }
353
354 /* At this point the changing of the current state modification
355 * flag is allowed. */
356 allowStateModification();
357
358 /* commit all changes made during the initialization */
359 commit();
360 }
361
362 /* Confirm a successful initialization when it's the case */
363 if (SUCCEEDED(rc))
364 {
365 if (mData->mAccessible)
366 autoInitSpan.setSucceeded();
367 else
368 autoInitSpan.setLimited();
369 }
370
371 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
372 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
373 mData->mRegistered,
374 mData->mAccessible,
375 rc));
376
377 LogFlowThisFuncLeave();
378
379 return rc;
380}
381
382/**
383 * Initializes a new instance with data from machine XML (formerly Init_Registered).
384 * Gets called in two modes:
385 *
386 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
387 * UUID is specified and we mark the machine as "registered";
388 *
389 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
390 * and the machine remains unregistered until RegisterMachine() is called.
391 *
392 * @param aParent Associated parent object
393 * @param aConfigFile Local file system path to the VM settings file (can
394 * be relative to the VirtualBox config directory).
395 * @param aId UUID of the machine or NULL (see above).
396 *
397 * @return Success indicator. if not S_OK, the machine object is invalid
398 */
399HRESULT Machine::initFromSettings(VirtualBox *aParent,
400 const Utf8Str &strConfigFile,
401 const Guid *aId)
402{
403 LogFlowThisFuncEnter();
404 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
405
406 /* Enclose the state transition NotReady->InInit->Ready */
407 AutoInitSpan autoInitSpan(this);
408 AssertReturn(autoInitSpan.isOk(), E_FAIL);
409
410 HRESULT rc = initImpl(aParent, strConfigFile);
411 if (FAILED(rc)) return rc;
412
413 if (aId)
414 {
415 // loading a registered VM:
416 unconst(mData->mUuid) = *aId;
417 mData->mRegistered = TRUE;
418 // now load the settings from XML:
419 rc = registeredInit();
420 // this calls initDataAndChildObjects() and loadSettings()
421 }
422 else
423 {
424 // opening an unregistered VM (VirtualBox::OpenMachine()):
425 rc = initDataAndChildObjects();
426
427 if (SUCCEEDED(rc))
428 {
429 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
430 mData->mAccessible = TRUE;
431
432 try
433 {
434 // load and parse machine XML; this will throw on XML or logic errors
435 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
436
437 // reject VM UUID duplicates, they can happen if someone
438 // tries to register an already known VM config again
439 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
440 true /* fPermitInaccessible */,
441 false /* aDoSetError */,
442 NULL) != VBOX_E_OBJECT_NOT_FOUND)
443 {
444 throw setError(E_FAIL,
445 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
446 mData->m_strConfigFile.c_str());
447 }
448
449 // use UUID from machine config
450 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
451
452 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
453 NULL /* puuidRegistry */);
454 if (FAILED(rc)) throw rc;
455
456 /* At this point the changing of the current state modification
457 * flag is allowed. */
458 allowStateModification();
459
460 commit();
461 }
462 catch (HRESULT err)
463 {
464 /* we assume that error info is set by the thrower */
465 rc = err;
466 }
467 catch (...)
468 {
469 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
470 }
471 }
472 }
473
474 /* Confirm a successful initialization when it's the case */
475 if (SUCCEEDED(rc))
476 {
477 if (mData->mAccessible)
478 autoInitSpan.setSucceeded();
479 else
480 {
481 autoInitSpan.setLimited();
482
483 // uninit media from this machine's media registry, or else
484 // reloading the settings will fail
485 mParent->unregisterMachineMedia(getId());
486 }
487 }
488
489 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
490 "rc=%08X\n",
491 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
492 mData->mRegistered, mData->mAccessible, rc));
493
494 LogFlowThisFuncLeave();
495
496 return rc;
497}
498
499/**
500 * Initializes a new instance from a machine config that is already in memory
501 * (import OVF case). Since we are importing, the UUID in the machine
502 * config is ignored and we always generate a fresh one.
503 *
504 * @param strName Name for the new machine; this overrides what is specified in config and is used
505 * for the settings file as well.
506 * @param config Machine configuration loaded and parsed from XML.
507 *
508 * @return Success indicator. if not S_OK, the machine object is invalid
509 */
510HRESULT Machine::init(VirtualBox *aParent,
511 const Utf8Str &strName,
512 const settings::MachineConfigFile &config)
513{
514 LogFlowThisFuncEnter();
515
516 /* Enclose the state transition NotReady->InInit->Ready */
517 AutoInitSpan autoInitSpan(this);
518 AssertReturn(autoInitSpan.isOk(), E_FAIL);
519
520 Utf8Str strConfigFile;
521 aParent->getDefaultMachineFolder(strConfigFile);
522 strConfigFile.append(RTPATH_DELIMITER);
523 strConfigFile.append(strName);
524 strConfigFile.append(RTPATH_DELIMITER);
525 strConfigFile.append(strName);
526 strConfigFile.append(".vbox");
527
528 HRESULT rc = initImpl(aParent, strConfigFile);
529 if (FAILED(rc)) return rc;
530
531 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
532 if (FAILED(rc)) return rc;
533
534 rc = initDataAndChildObjects();
535
536 if (SUCCEEDED(rc))
537 {
538 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
539 mData->mAccessible = TRUE;
540
541 // create empty machine config for instance data
542 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
543
544 // generate fresh UUID, ignore machine config
545 unconst(mData->mUuid).create();
546
547 rc = loadMachineDataFromSettings(config,
548 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
549
550 // override VM name as well, it may be different
551 mUserData->s.strName = strName;
552
553 if (SUCCEEDED(rc))
554 {
555 /* At this point the changing of the current state modification
556 * flag is allowed. */
557 allowStateModification();
558
559 /* commit all changes made during the initialization */
560 commit();
561 }
562 }
563
564 /* Confirm a successful initialization when it's the case */
565 if (SUCCEEDED(rc))
566 {
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 {
571 autoInitSpan.setLimited();
572
573 // uninit media from this machine's media registry, or else
574 // reloading the settings will fail
575 mParent->unregisterMachineMedia(getId());
576 }
577 }
578
579 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
580 "rc=%08X\n",
581 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
582 mData->mRegistered, mData->mAccessible, rc));
583
584 LogFlowThisFuncLeave();
585
586 return rc;
587}
588
589/**
590 * Shared code between the various init() implementations.
591 * @param aParent
592 * @return
593 */
594HRESULT Machine::initImpl(VirtualBox *aParent,
595 const Utf8Str &strConfigFile)
596{
597 LogFlowThisFuncEnter();
598
599 AssertReturn(aParent, E_INVALIDARG);
600 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
601
602 HRESULT rc = S_OK;
603
604 /* share the parent weakly */
605 unconst(mParent) = aParent;
606
607 /* allocate the essential machine data structure (the rest will be
608 * allocated later by initDataAndChildObjects() */
609 mData.allocate();
610
611 /* memorize the config file name (as provided) */
612 mData->m_strConfigFile = strConfigFile;
613
614 /* get the full file name */
615 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
616 if (RT_FAILURE(vrc1))
617 return setError(VBOX_E_FILE_ERROR,
618 tr("Invalid machine settings file name '%s' (%Rrc)"),
619 strConfigFile.c_str(),
620 vrc1);
621
622 LogFlowThisFuncLeave();
623
624 return rc;
625}
626
627/**
628 * Tries to create a machine settings file in the path stored in the machine
629 * instance data. Used when a new machine is created to fail gracefully if
630 * the settings file could not be written (e.g. because machine dir is read-only).
631 * @return
632 */
633HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
634{
635 HRESULT rc = S_OK;
636
637 // when we create a new machine, we must be able to create the settings file
638 RTFILE f = NIL_RTFILE;
639 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
640 if ( RT_SUCCESS(vrc)
641 || vrc == VERR_SHARING_VIOLATION
642 )
643 {
644 if (RT_SUCCESS(vrc))
645 RTFileClose(f);
646 if (!fForceOverwrite)
647 rc = setError(VBOX_E_FILE_ERROR,
648 tr("Machine settings file '%s' already exists"),
649 mData->m_strConfigFileFull.c_str());
650 else
651 {
652 /* try to delete the config file, as otherwise the creation
653 * of a new settings file will fail. */
654 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
655 if (RT_FAILURE(vrc2))
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Could not delete the existing settings file '%s' (%Rrc)"),
658 mData->m_strConfigFileFull.c_str(), vrc2);
659 }
660 }
661 else if ( vrc != VERR_FILE_NOT_FOUND
662 && vrc != VERR_PATH_NOT_FOUND
663 )
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Invalid machine settings file name '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(),
667 vrc);
668 return rc;
669}
670
671/**
672 * Initializes the registered machine by loading the settings file.
673 * This method is separated from #init() in order to make it possible to
674 * retry the operation after VirtualBox startup instead of refusing to
675 * startup the whole VirtualBox server in case if the settings file of some
676 * registered VM is invalid or inaccessible.
677 *
678 * @note Must be always called from this object's write lock
679 * (unless called from #init() that doesn't need any locking).
680 * @note Locks the mUSBController method for writing.
681 * @note Subclasses must not call this method.
682 */
683HRESULT Machine::registeredInit()
684{
685 AssertReturn(!isSessionMachine(), E_FAIL);
686 AssertReturn(!isSnapshotMachine(), E_FAIL);
687 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
688 AssertReturn(!mData->mAccessible, E_FAIL);
689
690 HRESULT rc = initDataAndChildObjects();
691
692 if (SUCCEEDED(rc))
693 {
694 /* Temporarily reset the registered flag in order to let setters
695 * potentially called from loadSettings() succeed (isMutable() used in
696 * all setters will return FALSE for a Machine instance if mRegistered
697 * is TRUE). */
698 mData->mRegistered = FALSE;
699
700 try
701 {
702 // load and parse machine XML; this will throw on XML or logic errors
703 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
704
705 if (mData->mUuid != mData->pMachineConfigFile->uuid)
706 throw setError(E_FAIL,
707 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
708 mData->pMachineConfigFile->uuid.raw(),
709 mData->m_strConfigFileFull.c_str(),
710 mData->mUuid.toString().c_str(),
711 mParent->settingsFilePath().c_str());
712
713 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
714 NULL /* const Guid *puuidRegistry */);
715 if (FAILED(rc)) throw rc;
716 }
717 catch (HRESULT err)
718 {
719 /* we assume that error info is set by the thrower */
720 rc = err;
721 }
722 catch (...)
723 {
724 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
725 }
726
727 /* Restore the registered flag (even on failure) */
728 mData->mRegistered = TRUE;
729 }
730
731 if (SUCCEEDED(rc))
732 {
733 /* Set mAccessible to TRUE only if we successfully locked and loaded
734 * the settings file */
735 mData->mAccessible = TRUE;
736
737 /* commit all changes made during loading the settings file */
738 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
739 /// @todo r=klaus for some reason the settings loading logic backs up
740 // the settings, and therefore a commit is needed. Should probably be changed.
741 }
742 else
743 {
744 /* If the machine is registered, then, instead of returning a
745 * failure, we mark it as inaccessible and set the result to
746 * success to give it a try later */
747
748 /* fetch the current error info */
749 mData->mAccessError = com::ErrorInfo();
750 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
751 mData->mUuid.raw(),
752 mData->mAccessError.getText().raw()));
753
754 /* rollback all changes */
755 rollback(false /* aNotify */);
756
757 // uninit media from this machine's media registry, or else
758 // reloading the settings will fail
759 mParent->unregisterMachineMedia(getId());
760
761 /* uninitialize the common part to make sure all data is reset to
762 * default (null) values */
763 uninitDataAndChildObjects();
764
765 rc = S_OK;
766 }
767
768 return rc;
769}
770
771/**
772 * Uninitializes the instance.
773 * Called either from FinalRelease() or by the parent when it gets destroyed.
774 *
775 * @note The caller of this method must make sure that this object
776 * a) doesn't have active callers on the current thread and b) is not locked
777 * by the current thread; otherwise uninit() will hang either a) due to
778 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
779 * a dead-lock caused by this thread waiting for all callers on the other
780 * threads are done but preventing them from doing so by holding a lock.
781 */
782void Machine::uninit()
783{
784 LogFlowThisFuncEnter();
785
786 Assert(!isWriteLockOnCurrentThread());
787
788 Assert(!uRegistryNeedsSaving);
789 if (uRegistryNeedsSaving)
790 {
791 AutoCaller autoCaller(this);
792 if (SUCCEEDED(autoCaller.rc()))
793 {
794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
795 saveSettings(NULL, Machine::SaveS_Force);
796 }
797 }
798
799 /* Enclose the state transition Ready->InUninit->NotReady */
800 AutoUninitSpan autoUninitSpan(this);
801 if (autoUninitSpan.uninitDone())
802 return;
803
804 Assert(!isSnapshotMachine());
805 Assert(!isSessionMachine());
806 Assert(!!mData);
807
808 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
809 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
810
811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
812
813 if (!mData->mSession.mMachine.isNull())
814 {
815 /* Theoretically, this can only happen if the VirtualBox server has been
816 * terminated while there were clients running that owned open direct
817 * sessions. Since in this case we are definitely called by
818 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
819 * won't happen on the client watcher thread (because it does
820 * VirtualBox::addCaller() for the duration of the
821 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
822 * cannot happen until the VirtualBox caller is released). This is
823 * important, because SessionMachine::uninit() cannot correctly operate
824 * after we return from this method (it expects the Machine instance is
825 * still valid). We'll call it ourselves below.
826 */
827 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
828 (SessionMachine*)mData->mSession.mMachine));
829
830 if (Global::IsOnlineOrTransient(mData->mMachineState))
831 {
832 LogWarningThisFunc(("Setting state to Aborted!\n"));
833 /* set machine state using SessionMachine reimplementation */
834 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
835 }
836
837 /*
838 * Uninitialize SessionMachine using public uninit() to indicate
839 * an unexpected uninitialization.
840 */
841 mData->mSession.mMachine->uninit();
842 /* SessionMachine::uninit() must set mSession.mMachine to null */
843 Assert(mData->mSession.mMachine.isNull());
844 }
845
846 // uninit media from this machine's media registry, if they're still there
847 Guid uuidMachine(getId());
848
849 /* the lock is no more necessary (SessionMachine is uninitialized) */
850 alock.release();
851
852 /* XXX This will fail with
853 * "cannot be closed because it is still attached to 1 virtual machines"
854 * because at this point we did not call uninitDataAndChildObjects() yet
855 * and therefore also removeBackReference() for all these mediums was not called! */
856 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
857 mParent->unregisterMachineMedia(uuidMachine);
858
859 // has machine been modified?
860 if (mData->flModifications)
861 {
862 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
863 rollback(false /* aNotify */);
864 }
865
866 if (mData->mAccessible)
867 uninitDataAndChildObjects();
868
869 /* free the essential data structure last */
870 mData.free();
871
872 LogFlowThisFuncLeave();
873}
874
875// IMachine properties
876/////////////////////////////////////////////////////////////////////////////
877
878STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
879{
880 CheckComArgOutPointerValid(aParent);
881
882 AutoLimitedCaller autoCaller(this);
883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
884
885 /* mParent is constant during life time, no need to lock */
886 ComObjPtr<VirtualBox> pVirtualBox(mParent);
887 pVirtualBox.queryInterfaceTo(aParent);
888
889 return S_OK;
890}
891
892STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
893{
894 CheckComArgOutPointerValid(aAccessible);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 LogFlowThisFunc(("ENTER\n"));
900
901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
902
903 HRESULT rc = S_OK;
904
905 if (!mData->mAccessible)
906 {
907 /* try to initialize the VM once more if not accessible */
908
909 AutoReinitSpan autoReinitSpan(this);
910 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
911
912#ifdef DEBUG
913 LogFlowThisFunc(("Dumping media backreferences\n"));
914 mParent->dumpAllBackRefs();
915#endif
916
917 if (mData->pMachineConfigFile)
918 {
919 // reset the XML file to force loadSettings() (called from registeredInit())
920 // to parse it again; the file might have changed
921 delete mData->pMachineConfigFile;
922 mData->pMachineConfigFile = NULL;
923 }
924
925 rc = registeredInit();
926
927 if (SUCCEEDED(rc) && mData->mAccessible)
928 {
929 autoReinitSpan.setSucceeded();
930
931 /* make sure interesting parties will notice the accessibility
932 * state change */
933 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
934 mParent->onMachineDataChange(mData->mUuid);
935 }
936 }
937
938 if (SUCCEEDED(rc))
939 *aAccessible = mData->mAccessible;
940
941 LogFlowThisFuncLeave();
942
943 return rc;
944}
945
946STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
947{
948 CheckComArgOutPointerValid(aAccessError);
949
950 AutoLimitedCaller autoCaller(this);
951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
952
953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
954
955 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
956 {
957 /* return shortly */
958 aAccessError = NULL;
959 return S_OK;
960 }
961
962 HRESULT rc = S_OK;
963
964 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
965 rc = errorInfo.createObject();
966 if (SUCCEEDED(rc))
967 {
968 errorInfo->init(mData->mAccessError.getResultCode(),
969 mData->mAccessError.getInterfaceID().ref(),
970 Utf8Str(mData->mAccessError.getComponent()).c_str(),
971 Utf8Str(mData->mAccessError.getText()));
972 rc = errorInfo.queryInterfaceTo(aAccessError);
973 }
974
975 return rc;
976}
977
978STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
979{
980 CheckComArgOutPointerValid(aName);
981
982 AutoCaller autoCaller(this);
983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
984
985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
986
987 mUserData->s.strName.cloneTo(aName);
988
989 return S_OK;
990}
991
992STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
993{
994 CheckComArgStrNotEmptyOrNull(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 // prohibit setting a UUID only as the machine name, or else it can
1000 // never be found by findMachine()
1001 Guid test(aName);
1002 if (test.isNotEmpty())
1003 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1004
1005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1006
1007 HRESULT rc = checkStateDependency(MutableStateDep);
1008 if (FAILED(rc)) return rc;
1009
1010 setModified(IsModified_MachineData);
1011 mUserData.backup();
1012 mUserData->s.strName = aName;
1013
1014 return S_OK;
1015}
1016
1017STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1018{
1019 CheckComArgOutPointerValid(aDescription);
1020
1021 AutoCaller autoCaller(this);
1022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1023
1024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 mUserData->s.strDescription.cloneTo(aDescription);
1027
1028 return S_OK;
1029}
1030
1031STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1032{
1033 AutoCaller autoCaller(this);
1034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1035
1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 // this can be done in principle in any state as it doesn't affect the VM
1039 // significantly, but play safe by not messing around while complex
1040 // activities are going on
1041 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1042 if (FAILED(rc)) return rc;
1043
1044 setModified(IsModified_MachineData);
1045 mUserData.backup();
1046 mUserData->s.strDescription = aDescription;
1047
1048 return S_OK;
1049}
1050
1051STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1052{
1053 CheckComArgOutPointerValid(aId);
1054
1055 AutoLimitedCaller autoCaller(this);
1056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1057
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059
1060 mData->mUuid.toUtf16().cloneTo(aId);
1061
1062 return S_OK;
1063}
1064
1065STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1066{
1067 CheckComArgOutSafeArrayPointerValid(aGroups);
1068
1069 AutoCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1074 size_t i = 0;
1075 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1076 it != mUserData->s.llGroups.end();
1077 ++it, i++)
1078 {
1079 Bstr tmp = *it;
1080 tmp.cloneTo(&groups[i]);
1081 }
1082 groups.detachTo(ComSafeArrayOutArg(aGroups));
1083
1084 return S_OK;
1085}
1086
1087STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1088{
1089 AutoCaller autoCaller(this);
1090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1091
1092 StringsList llGroups;
1093 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1094 if (FAILED(rc))
1095 return rc;
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 // changing machine groups is possible while the VM is offline
1100 rc = checkStateDependency(OfflineStateDep);
1101 if (FAILED(rc)) return rc;
1102
1103 setModified(IsModified_MachineData);
1104 mUserData.backup();
1105 mUserData->s.llGroups = llGroups;
1106
1107 return S_OK;
1108}
1109
1110STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1111{
1112 CheckComArgOutPointerValid(aOSTypeId);
1113
1114 AutoCaller autoCaller(this);
1115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1116
1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 mUserData->s.strOsType.cloneTo(aOSTypeId);
1120
1121 return S_OK;
1122}
1123
1124STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1125{
1126 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1127
1128 AutoCaller autoCaller(this);
1129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1130
1131 /* look up the object by Id to check it is valid */
1132 ComPtr<IGuestOSType> guestOSType;
1133 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1134 if (FAILED(rc)) return rc;
1135
1136 /* when setting, always use the "etalon" value for consistency -- lookup
1137 * by ID is case-insensitive and the input value may have different case */
1138 Bstr osTypeId;
1139 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1140 if (FAILED(rc)) return rc;
1141
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 rc = checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 setModified(IsModified_MachineData);
1148 mUserData.backup();
1149 mUserData->s.strOsType = osTypeId;
1150
1151 return S_OK;
1152}
1153
1154
1155STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1156{
1157 CheckComArgOutPointerValid(aFirmwareType);
1158
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aFirmwareType = mHWData->mFirmwareType;
1165
1166 return S_OK;
1167}
1168
1169STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1170{
1171 AutoCaller autoCaller(this);
1172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT rc = checkStateDependency(MutableStateDep);
1176 if (FAILED(rc)) return rc;
1177
1178 setModified(IsModified_MachineData);
1179 mHWData.backup();
1180 mHWData->mFirmwareType = aFirmwareType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1186{
1187 CheckComArgOutPointerValid(aKeyboardHIDType);
1188
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1200{
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 HRESULT rc = checkStateDependency(MutableStateDep);
1206 if (FAILED(rc)) return rc;
1207
1208 setModified(IsModified_MachineData);
1209 mHWData.backup();
1210 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1211
1212 return S_OK;
1213}
1214
1215STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1216{
1217 CheckComArgOutPointerValid(aPointingHIDType);
1218
1219 AutoCaller autoCaller(this);
1220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1221
1222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 *aPointingHIDType = mHWData->mPointingHIDType;
1225
1226 return S_OK;
1227}
1228
1229STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1230{
1231 AutoCaller autoCaller(this);
1232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 HRESULT rc = checkStateDependency(MutableStateDep);
1236 if (FAILED(rc)) return rc;
1237
1238 setModified(IsModified_MachineData);
1239 mHWData.backup();
1240 mHWData->mPointingHIDType = aPointingHIDType;
1241
1242 return S_OK;
1243}
1244
1245STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1246{
1247 CheckComArgOutPointerValid(aChipsetType);
1248
1249 AutoCaller autoCaller(this);
1250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1251
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aChipsetType = mHWData->mChipsetType;
1255
1256 return S_OK;
1257}
1258
1259STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1260{
1261 AutoCaller autoCaller(this);
1262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT rc = checkStateDependency(MutableStateDep);
1266 if (FAILED(rc)) return rc;
1267
1268 if (aChipsetType != mHWData->mChipsetType)
1269 {
1270 setModified(IsModified_MachineData);
1271 mHWData.backup();
1272 mHWData->mChipsetType = aChipsetType;
1273
1274 // Resize network adapter array, to be finalized on commit/rollback.
1275 // We must not throw away entries yet, otherwise settings are lost
1276 // without a way to roll back.
1277 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1278 uint32_t oldCount = mNetworkAdapters.size();
1279 if (newCount > oldCount)
1280 {
1281 mNetworkAdapters.resize(newCount);
1282 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1283 {
1284 unconst(mNetworkAdapters[slot]).createObject();
1285 mNetworkAdapters[slot]->init(this, slot);
1286 }
1287 }
1288 }
1289
1290 return S_OK;
1291}
1292
1293STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1294{
1295 CheckComArgOutPointerValid(aHWVersion);
1296
1297 AutoCaller autoCaller(this);
1298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1299
1300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1301
1302 mHWData->mHWVersion.cloneTo(aHWVersion);
1303
1304 return S_OK;
1305}
1306
1307STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1308{
1309 /* check known version */
1310 Utf8Str hwVersion = aHWVersion;
1311 if ( hwVersion.compare("1") != 0
1312 && hwVersion.compare("2") != 0)
1313 return setError(E_INVALIDARG,
1314 tr("Invalid hardware version: %ls\n"), aHWVersion);
1315
1316 AutoCaller autoCaller(this);
1317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1318
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 HRESULT rc = checkStateDependency(MutableStateDep);
1322 if (FAILED(rc)) return rc;
1323
1324 setModified(IsModified_MachineData);
1325 mHWData.backup();
1326 mHWData->mHWVersion = hwVersion;
1327
1328 return S_OK;
1329}
1330
1331STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1332{
1333 CheckComArgOutPointerValid(aUUID);
1334
1335 AutoCaller autoCaller(this);
1336 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1337
1338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1339
1340 if (!mHWData->mHardwareUUID.isEmpty())
1341 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1342 else
1343 mData->mUuid.toUtf16().cloneTo(aUUID);
1344
1345 return S_OK;
1346}
1347
1348STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1349{
1350 Guid hardwareUUID(aUUID);
1351 if (hardwareUUID.isEmpty())
1352 return E_INVALIDARG;
1353
1354 AutoCaller autoCaller(this);
1355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1356
1357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1358
1359 HRESULT rc = checkStateDependency(MutableStateDep);
1360 if (FAILED(rc)) return rc;
1361
1362 setModified(IsModified_MachineData);
1363 mHWData.backup();
1364 if (hardwareUUID == mData->mUuid)
1365 mHWData->mHardwareUUID.clear();
1366 else
1367 mHWData->mHardwareUUID = hardwareUUID;
1368
1369 return S_OK;
1370}
1371
1372STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1373{
1374 CheckComArgOutPointerValid(memorySize);
1375
1376 AutoCaller autoCaller(this);
1377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1378
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 *memorySize = mHWData->mMemorySize;
1382
1383 return S_OK;
1384}
1385
1386STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1387{
1388 /* check RAM limits */
1389 if ( memorySize < MM_RAM_MIN_IN_MB
1390 || memorySize > MM_RAM_MAX_IN_MB
1391 )
1392 return setError(E_INVALIDARG,
1393 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1394 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1395
1396 AutoCaller autoCaller(this);
1397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1398
1399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 HRESULT rc = checkStateDependency(MutableStateDep);
1402 if (FAILED(rc)) return rc;
1403
1404 setModified(IsModified_MachineData);
1405 mHWData.backup();
1406 mHWData->mMemorySize = memorySize;
1407
1408 return S_OK;
1409}
1410
1411STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1412{
1413 CheckComArgOutPointerValid(CPUCount);
1414
1415 AutoCaller autoCaller(this);
1416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1417
1418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 *CPUCount = mHWData->mCPUCount;
1421
1422 return S_OK;
1423}
1424
1425STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1426{
1427 /* check CPU limits */
1428 if ( CPUCount < SchemaDefs::MinCPUCount
1429 || CPUCount > SchemaDefs::MaxCPUCount
1430 )
1431 return setError(E_INVALIDARG,
1432 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1433 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1434
1435 AutoCaller autoCaller(this);
1436 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1437
1438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1441 if (mHWData->mCPUHotPlugEnabled)
1442 {
1443 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1444 {
1445 if (mHWData->mCPUAttached[idx])
1446 return setError(E_INVALIDARG,
1447 tr("There is still a CPU attached to socket %lu."
1448 "Detach the CPU before removing the socket"),
1449 CPUCount, idx+1);
1450 }
1451 }
1452
1453 HRESULT rc = checkStateDependency(MutableStateDep);
1454 if (FAILED(rc)) return rc;
1455
1456 setModified(IsModified_MachineData);
1457 mHWData.backup();
1458 mHWData->mCPUCount = CPUCount;
1459
1460 return S_OK;
1461}
1462
1463STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1464{
1465 CheckComArgOutPointerValid(aExecutionCap);
1466
1467 AutoCaller autoCaller(this);
1468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1469
1470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 *aExecutionCap = mHWData->mCpuExecutionCap;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1478{
1479 HRESULT rc = S_OK;
1480
1481 /* check throttle limits */
1482 if ( aExecutionCap < 1
1483 || aExecutionCap > 100
1484 )
1485 return setError(E_INVALIDARG,
1486 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1487 aExecutionCap, 1, 100);
1488
1489 AutoCaller autoCaller(this);
1490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1491
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 alock.release();
1495 rc = onCPUExecutionCapChange(aExecutionCap);
1496 alock.acquire();
1497 if (FAILED(rc)) return rc;
1498
1499 setModified(IsModified_MachineData);
1500 mHWData.backup();
1501 mHWData->mCpuExecutionCap = aExecutionCap;
1502
1503 /* Save settings if online - todo why is this required?? */
1504 if (Global::IsOnline(mData->mMachineState))
1505 saveSettings(NULL);
1506
1507 return S_OK;
1508}
1509
1510
1511STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1512{
1513 CheckComArgOutPointerValid(enabled);
1514
1515 AutoCaller autoCaller(this);
1516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1517
1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1519
1520 *enabled = mHWData->mCPUHotPlugEnabled;
1521
1522 return S_OK;
1523}
1524
1525STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1526{
1527 HRESULT rc = S_OK;
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 rc = checkStateDependency(MutableStateDep);
1535 if (FAILED(rc)) return rc;
1536
1537 if (mHWData->mCPUHotPlugEnabled != enabled)
1538 {
1539 if (enabled)
1540 {
1541 setModified(IsModified_MachineData);
1542 mHWData.backup();
1543
1544 /* Add the amount of CPUs currently attached */
1545 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1546 {
1547 mHWData->mCPUAttached[i] = true;
1548 }
1549 }
1550 else
1551 {
1552 /*
1553 * We can disable hotplug only if the amount of maximum CPUs is equal
1554 * to the amount of attached CPUs
1555 */
1556 unsigned cCpusAttached = 0;
1557 unsigned iHighestId = 0;
1558
1559 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1560 {
1561 if (mHWData->mCPUAttached[i])
1562 {
1563 cCpusAttached++;
1564 iHighestId = i;
1565 }
1566 }
1567
1568 if ( (cCpusAttached != mHWData->mCPUCount)
1569 || (iHighestId >= mHWData->mCPUCount))
1570 return setError(E_INVALIDARG,
1571 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1572
1573 setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 }
1576 }
1577
1578 mHWData->mCPUHotPlugEnabled = enabled;
1579
1580 return rc;
1581}
1582
1583STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1584{
1585#ifdef VBOX_WITH_USB_CARDREADER
1586 CheckComArgOutPointerValid(enabled);
1587
1588 AutoCaller autoCaller(this);
1589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1590
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1594
1595 return S_OK;
1596#else
1597 NOREF(enabled);
1598 return E_NOTIMPL;
1599#endif
1600}
1601
1602STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1603{
1604#ifdef VBOX_WITH_USB_CARDREADER
1605 AutoCaller autoCaller(this);
1606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1608
1609 HRESULT rc = checkStateDependency(MutableStateDep);
1610 if (FAILED(rc)) return rc;
1611
1612 setModified(IsModified_MachineData);
1613 mHWData.backup();
1614 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1615
1616 return S_OK;
1617#else
1618 NOREF(enabled);
1619 return E_NOTIMPL;
1620#endif
1621}
1622
1623STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1624{
1625 NOREF(enabled);
1626 return E_NOTIMPL;
1627}
1628
1629STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1630{
1631 NOREF(enabled);
1632 return E_NOTIMPL;
1633}
1634
1635STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1636{
1637 CheckComArgOutPointerValid(enabled);
1638
1639 AutoCaller autoCaller(this);
1640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *enabled = mHWData->mHPETEnabled;
1644
1645 return S_OK;
1646}
1647
1648STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1649{
1650 HRESULT rc = S_OK;
1651
1652 AutoCaller autoCaller(this);
1653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 rc = checkStateDependency(MutableStateDep);
1657 if (FAILED(rc)) return rc;
1658
1659 setModified(IsModified_MachineData);
1660 mHWData.backup();
1661
1662 mHWData->mHPETEnabled = enabled;
1663
1664 return rc;
1665}
1666
1667STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled)
1668{
1669 AutoCaller autoCaller(this);
1670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1671
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *fEnabled = mHWData->mVideoCaptureEnabled;
1675 return S_OK;
1676}
1677
1678STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1679{
1680 AutoCaller autoCaller(this);
1681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1682
1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1684 mHWData->mVideoCaptureEnabled = fEnabled;
1685 return S_OK;
1686}
1687
1688STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1689{
1690 AutoCaller autoCaller(this);
1691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1692
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 mHWData->mVideoCaptureFile.cloneTo(apFile);
1695 return S_OK;
1696}
1697
1698STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1699{
1700 Utf8Str strFile(aFile);
1701 AutoCaller autoCaller(this);
1702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705 if (strFile.isEmpty())
1706 strFile = "VideoCap.webm";
1707 mHWData->mVideoCaptureFile = strFile;
1708 return S_OK;
1709}
1710
1711
1712STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1713{
1714 AutoCaller autoCaller(this);
1715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1716
1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1718 *ulHorzRes = mHWData->mVideoCaptureWidth;
1719 return S_OK;
1720}
1721
1722STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1723{
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc()))
1726 {
1727 LogFlow(("Autolocked failed\n"));
1728 return autoCaller.rc();
1729 }
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732 mHWData->mVideoCaptureWidth = ulHorzRes;
1733 return S_OK;
1734}
1735
1736STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1737{
1738 AutoCaller autoCaller(this);
1739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1740
1741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1742 *ulVertRes = mHWData->mVideoCaptureHeight;
1743 return S_OK;
1744}
1745
1746STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1747{
1748 AutoCaller autoCaller(this);
1749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1750
1751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1752 mHWData->mVideoCaptureHeight = ulVertRes;
1753 return S_OK;
1754}
1755
1756STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1757{
1758 CheckComArgOutPointerValid(memorySize);
1759
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 *memorySize = mHWData->mVRAMSize;
1766
1767 return S_OK;
1768}
1769
1770STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1771{
1772 /* check VRAM limits */
1773 if (memorySize < SchemaDefs::MinGuestVRAM ||
1774 memorySize > SchemaDefs::MaxGuestVRAM)
1775 return setError(E_INVALIDARG,
1776 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1777 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1778
1779 AutoCaller autoCaller(this);
1780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1781
1782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 HRESULT rc = checkStateDependency(MutableStateDep);
1785 if (FAILED(rc)) return rc;
1786
1787 setModified(IsModified_MachineData);
1788 mHWData.backup();
1789 mHWData->mVRAMSize = memorySize;
1790
1791 return S_OK;
1792}
1793
1794/** @todo this method should not be public */
1795STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1796{
1797 CheckComArgOutPointerValid(memoryBalloonSize);
1798
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1805
1806 return S_OK;
1807}
1808
1809/**
1810 * Set the memory balloon size.
1811 *
1812 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1813 * we have to make sure that we never call IGuest from here.
1814 */
1815STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1816{
1817 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1818#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1819 /* check limits */
1820 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1821 return setError(E_INVALIDARG,
1822 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1823 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1824
1825 AutoCaller autoCaller(this);
1826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1827
1828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1829
1830 setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1833
1834 return S_OK;
1835#else
1836 NOREF(memoryBalloonSize);
1837 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1838#endif
1839}
1840
1841STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1842{
1843 CheckComArgOutPointerValid(enabled);
1844
1845 AutoCaller autoCaller(this);
1846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1847
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 *enabled = mHWData->mPageFusionEnabled;
1851 return S_OK;
1852}
1853
1854STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1855{
1856#ifdef VBOX_WITH_PAGE_SHARING
1857 AutoCaller autoCaller(this);
1858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1859
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1863 setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mPageFusionEnabled = enabled;
1866 return S_OK;
1867#else
1868 NOREF(enabled);
1869 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1870#endif
1871}
1872
1873STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1874{
1875 CheckComArgOutPointerValid(enabled);
1876
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 *enabled = mHWData->mAccelerate3DEnabled;
1883
1884 return S_OK;
1885}
1886
1887STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1888{
1889 AutoCaller autoCaller(this);
1890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1891
1892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1893
1894 HRESULT rc = checkStateDependency(MutableStateDep);
1895 if (FAILED(rc)) return rc;
1896
1897 /** @todo check validity! */
1898
1899 setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mAccelerate3DEnabled = enable;
1902
1903 return S_OK;
1904}
1905
1906
1907STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1908{
1909 CheckComArgOutPointerValid(enabled);
1910
1911 AutoCaller autoCaller(this);
1912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1913
1914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1915
1916 *enabled = mHWData->mAccelerate2DVideoEnabled;
1917
1918 return S_OK;
1919}
1920
1921STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1922{
1923 AutoCaller autoCaller(this);
1924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1925
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 HRESULT rc = checkStateDependency(MutableStateDep);
1929 if (FAILED(rc)) return rc;
1930
1931 /** @todo check validity! */
1932
1933 setModified(IsModified_MachineData);
1934 mHWData.backup();
1935 mHWData->mAccelerate2DVideoEnabled = enable;
1936
1937 return S_OK;
1938}
1939
1940STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1941{
1942 CheckComArgOutPointerValid(monitorCount);
1943
1944 AutoCaller autoCaller(this);
1945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1946
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 *monitorCount = mHWData->mMonitorCount;
1950
1951 return S_OK;
1952}
1953
1954STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1955{
1956 /* make sure monitor count is a sensible number */
1957 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1958 return setError(E_INVALIDARG,
1959 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1960 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1961
1962 AutoCaller autoCaller(this);
1963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1964
1965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
1967 HRESULT rc = checkStateDependency(MutableStateDep);
1968 if (FAILED(rc)) return rc;
1969
1970 setModified(IsModified_MachineData);
1971 mHWData.backup();
1972 mHWData->mMonitorCount = monitorCount;
1973
1974 return S_OK;
1975}
1976
1977STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1978{
1979 CheckComArgOutPointerValid(biosSettings);
1980
1981 AutoCaller autoCaller(this);
1982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1983
1984 /* mBIOSSettings is constant during life time, no need to lock */
1985 mBIOSSettings.queryInterfaceTo(biosSettings);
1986
1987 return S_OK;
1988}
1989
1990STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1991{
1992 CheckComArgOutPointerValid(aVal);
1993
1994 AutoCaller autoCaller(this);
1995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1996
1997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 switch(property)
2000 {
2001 case CPUPropertyType_PAE:
2002 *aVal = mHWData->mPAEEnabled;
2003 break;
2004
2005 case CPUPropertyType_Synthetic:
2006 *aVal = mHWData->mSyntheticCpu;
2007 break;
2008
2009 default:
2010 return E_INVALIDARG;
2011 }
2012 return S_OK;
2013}
2014
2015STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2016{
2017 AutoCaller autoCaller(this);
2018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2019
2020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 HRESULT rc = checkStateDependency(MutableStateDep);
2023 if (FAILED(rc)) return rc;
2024
2025 switch(property)
2026 {
2027 case CPUPropertyType_PAE:
2028 setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mPAEEnabled = !!aVal;
2031 break;
2032
2033 case CPUPropertyType_Synthetic:
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mSyntheticCpu = !!aVal;
2037 break;
2038
2039 default:
2040 return E_INVALIDARG;
2041 }
2042 return S_OK;
2043}
2044
2045STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2046{
2047 CheckComArgOutPointerValid(aValEax);
2048 CheckComArgOutPointerValid(aValEbx);
2049 CheckComArgOutPointerValid(aValEcx);
2050 CheckComArgOutPointerValid(aValEdx);
2051
2052 AutoCaller autoCaller(this);
2053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2054
2055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2056
2057 switch(aId)
2058 {
2059 case 0x0:
2060 case 0x1:
2061 case 0x2:
2062 case 0x3:
2063 case 0x4:
2064 case 0x5:
2065 case 0x6:
2066 case 0x7:
2067 case 0x8:
2068 case 0x9:
2069 case 0xA:
2070 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2071 return E_INVALIDARG;
2072
2073 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2074 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2075 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2076 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2077 break;
2078
2079 case 0x80000000:
2080 case 0x80000001:
2081 case 0x80000002:
2082 case 0x80000003:
2083 case 0x80000004:
2084 case 0x80000005:
2085 case 0x80000006:
2086 case 0x80000007:
2087 case 0x80000008:
2088 case 0x80000009:
2089 case 0x8000000A:
2090 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2091 return E_INVALIDARG;
2092
2093 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2094 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2095 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2096 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2097 break;
2098
2099 default:
2100 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2101 }
2102 return S_OK;
2103}
2104
2105STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2106{
2107 AutoCaller autoCaller(this);
2108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2109
2110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 HRESULT rc = checkStateDependency(MutableStateDep);
2113 if (FAILED(rc)) return rc;
2114
2115 switch(aId)
2116 {
2117 case 0x0:
2118 case 0x1:
2119 case 0x2:
2120 case 0x3:
2121 case 0x4:
2122 case 0x5:
2123 case 0x6:
2124 case 0x7:
2125 case 0x8:
2126 case 0x9:
2127 case 0xA:
2128 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2129 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2130 setModified(IsModified_MachineData);
2131 mHWData.backup();
2132 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2133 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2134 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2135 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2136 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2137 break;
2138
2139 case 0x80000000:
2140 case 0x80000001:
2141 case 0x80000002:
2142 case 0x80000003:
2143 case 0x80000004:
2144 case 0x80000005:
2145 case 0x80000006:
2146 case 0x80000007:
2147 case 0x80000008:
2148 case 0x80000009:
2149 case 0x8000000A:
2150 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2151 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2152 setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2155 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2157 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2158 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2159 break;
2160
2161 default:
2162 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2163 }
2164 return S_OK;
2165}
2166
2167STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2168{
2169 AutoCaller autoCaller(this);
2170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2171
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 switch(aId)
2178 {
2179 case 0x0:
2180 case 0x1:
2181 case 0x2:
2182 case 0x3:
2183 case 0x4:
2184 case 0x5:
2185 case 0x6:
2186 case 0x7:
2187 case 0x8:
2188 case 0x9:
2189 case 0xA:
2190 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2191 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2192 setModified(IsModified_MachineData);
2193 mHWData.backup();
2194 /* Invalidate leaf. */
2195 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2196 break;
2197
2198 case 0x80000000:
2199 case 0x80000001:
2200 case 0x80000002:
2201 case 0x80000003:
2202 case 0x80000004:
2203 case 0x80000005:
2204 case 0x80000006:
2205 case 0x80000007:
2206 case 0x80000008:
2207 case 0x80000009:
2208 case 0x8000000A:
2209 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2210 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2211 setModified(IsModified_MachineData);
2212 mHWData.backup();
2213 /* Invalidate leaf. */
2214 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2215 break;
2216
2217 default:
2218 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2219 }
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2224{
2225 AutoCaller autoCaller(this);
2226 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2227
2228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2229
2230 HRESULT rc = checkStateDependency(MutableStateDep);
2231 if (FAILED(rc)) return rc;
2232
2233 setModified(IsModified_MachineData);
2234 mHWData.backup();
2235
2236 /* Invalidate all standard leafs. */
2237 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2238 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2239
2240 /* Invalidate all extended leafs. */
2241 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2242 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2243
2244 return S_OK;
2245}
2246
2247STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2248{
2249 CheckComArgOutPointerValid(aVal);
2250
2251 AutoCaller autoCaller(this);
2252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2253
2254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2255
2256 switch(property)
2257 {
2258 case HWVirtExPropertyType_Enabled:
2259 *aVal = mHWData->mHWVirtExEnabled;
2260 break;
2261
2262 case HWVirtExPropertyType_Exclusive:
2263 *aVal = mHWData->mHWVirtExExclusive;
2264 break;
2265
2266 case HWVirtExPropertyType_VPID:
2267 *aVal = mHWData->mHWVirtExVPIDEnabled;
2268 break;
2269
2270 case HWVirtExPropertyType_NestedPaging:
2271 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2272 break;
2273
2274 case HWVirtExPropertyType_LargePages:
2275 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2276#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2277 *aVal = FALSE;
2278#endif
2279 break;
2280
2281 case HWVirtExPropertyType_Force:
2282 *aVal = mHWData->mHWVirtExForceEnabled;
2283 break;
2284
2285 default:
2286 return E_INVALIDARG;
2287 }
2288 return S_OK;
2289}
2290
2291STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2292{
2293 AutoCaller autoCaller(this);
2294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2295
2296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2297
2298 HRESULT rc = checkStateDependency(MutableStateDep);
2299 if (FAILED(rc)) return rc;
2300
2301 switch(property)
2302 {
2303 case HWVirtExPropertyType_Enabled:
2304 setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mHWVirtExEnabled = !!aVal;
2307 break;
2308
2309 case HWVirtExPropertyType_Exclusive:
2310 setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mHWVirtExExclusive = !!aVal;
2313 break;
2314
2315 case HWVirtExPropertyType_VPID:
2316 setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2319 break;
2320
2321 case HWVirtExPropertyType_NestedPaging:
2322 setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2325 break;
2326
2327 case HWVirtExPropertyType_LargePages:
2328 setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2331 break;
2332
2333 case HWVirtExPropertyType_Force:
2334 setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mHWVirtExForceEnabled = !!aVal;
2337 break;
2338
2339 default:
2340 return E_INVALIDARG;
2341 }
2342
2343 return S_OK;
2344}
2345
2346STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2347{
2348 CheckComArgOutPointerValid(aSnapshotFolder);
2349
2350 AutoCaller autoCaller(this);
2351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2352
2353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 Utf8Str strFullSnapshotFolder;
2356 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2357 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2358
2359 return S_OK;
2360}
2361
2362STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2363{
2364 /* @todo (r=dmik):
2365 * 1. Allow to change the name of the snapshot folder containing snapshots
2366 * 2. Rename the folder on disk instead of just changing the property
2367 * value (to be smart and not to leave garbage). Note that it cannot be
2368 * done here because the change may be rolled back. Thus, the right
2369 * place is #saveSettings().
2370 */
2371
2372 AutoCaller autoCaller(this);
2373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2374
2375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2376
2377 HRESULT rc = checkStateDependency(MutableStateDep);
2378 if (FAILED(rc)) return rc;
2379
2380 if (!mData->mCurrentSnapshot.isNull())
2381 return setError(E_FAIL,
2382 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2383
2384 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2385
2386 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2387 if (strSnapshotFolder.isEmpty())
2388 strSnapshotFolder = "Snapshots";
2389 int vrc = calculateFullPath(strSnapshotFolder,
2390 strSnapshotFolder);
2391 if (RT_FAILURE(vrc))
2392 return setError(E_FAIL,
2393 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2394 aSnapshotFolder, vrc);
2395
2396 setModified(IsModified_MachineData);
2397 mUserData.backup();
2398
2399 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2400
2401 return S_OK;
2402}
2403
2404STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2405{
2406 CheckComArgOutSafeArrayPointerValid(aAttachments);
2407
2408 AutoCaller autoCaller(this);
2409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2410
2411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2414 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2415
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2420{
2421 CheckComArgOutPointerValid(vrdeServer);
2422
2423 AutoCaller autoCaller(this);
2424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2425
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 Assert(!!mVRDEServer);
2429 mVRDEServer.queryInterfaceTo(vrdeServer);
2430
2431 return S_OK;
2432}
2433
2434STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2435{
2436 CheckComArgOutPointerValid(audioAdapter);
2437
2438 AutoCaller autoCaller(this);
2439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2440
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 mAudioAdapter.queryInterfaceTo(audioAdapter);
2444 return S_OK;
2445}
2446
2447STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2448{
2449#ifdef VBOX_WITH_VUSB
2450 CheckComArgOutPointerValid(aUSBController);
2451
2452 AutoCaller autoCaller(this);
2453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2454
2455 clearError();
2456 MultiResult rc(S_OK);
2457
2458# ifdef VBOX_WITH_USB
2459 rc = mParent->host()->checkUSBProxyService();
2460 if (FAILED(rc)) return rc;
2461# endif
2462
2463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 return rc = mUSBController.queryInterfaceTo(aUSBController);
2466#else
2467 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2468 * extended error info to indicate that USB is simply not available
2469 * (w/o treating it as a failure), for example, as in OSE */
2470 NOREF(aUSBController);
2471 ReturnComNotImplemented();
2472#endif /* VBOX_WITH_VUSB */
2473}
2474
2475STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2476{
2477 CheckComArgOutPointerValid(aFilePath);
2478
2479 AutoLimitedCaller autoCaller(this);
2480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2481
2482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 mData->m_strConfigFileFull.cloneTo(aFilePath);
2485 return S_OK;
2486}
2487
2488STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2489{
2490 CheckComArgOutPointerValid(aModified);
2491
2492 AutoCaller autoCaller(this);
2493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2494
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 HRESULT rc = checkStateDependency(MutableStateDep);
2498 if (FAILED(rc)) return rc;
2499
2500 if (!mData->pMachineConfigFile->fileExists())
2501 // this is a new machine, and no config file exists yet:
2502 *aModified = TRUE;
2503 else
2504 *aModified = (mData->flModifications != 0);
2505
2506 return S_OK;
2507}
2508
2509STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2510{
2511 CheckComArgOutPointerValid(aSessionState);
2512
2513 AutoCaller autoCaller(this);
2514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 *aSessionState = mData->mSession.mState;
2519
2520 return S_OK;
2521}
2522
2523STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2524{
2525 CheckComArgOutPointerValid(aSessionType);
2526
2527 AutoCaller autoCaller(this);
2528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2529
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 mData->mSession.mType.cloneTo(aSessionType);
2533
2534 return S_OK;
2535}
2536
2537STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2538{
2539 CheckComArgOutPointerValid(aSessionPID);
2540
2541 AutoCaller autoCaller(this);
2542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2543
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 *aSessionPID = mData->mSession.mPID;
2547
2548 return S_OK;
2549}
2550
2551STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2552{
2553 CheckComArgOutPointerValid(machineState);
2554
2555 AutoCaller autoCaller(this);
2556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2557
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 *machineState = mData->mMachineState;
2561
2562 return S_OK;
2563}
2564
2565STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2566{
2567 CheckComArgOutPointerValid(aLastStateChange);
2568
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2575
2576 return S_OK;
2577}
2578
2579STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2580{
2581 CheckComArgOutPointerValid(aStateFilePath);
2582
2583 AutoCaller autoCaller(this);
2584 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2585
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2589
2590 return S_OK;
2591}
2592
2593STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2594{
2595 CheckComArgOutPointerValid(aLogFolder);
2596
2597 AutoCaller autoCaller(this);
2598 AssertComRCReturnRC(autoCaller.rc());
2599
2600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602 Utf8Str logFolder;
2603 getLogFolder(logFolder);
2604 logFolder.cloneTo(aLogFolder);
2605
2606 return S_OK;
2607}
2608
2609STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2610{
2611 CheckComArgOutPointerValid(aCurrentSnapshot);
2612
2613 AutoCaller autoCaller(this);
2614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2615
2616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2619
2620 return S_OK;
2621}
2622
2623STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2624{
2625 CheckComArgOutPointerValid(aSnapshotCount);
2626
2627 AutoCaller autoCaller(this);
2628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2633 ? 0
2634 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2635
2636 return S_OK;
2637}
2638
2639STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2640{
2641 CheckComArgOutPointerValid(aCurrentStateModified);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 /* Note: for machines with no snapshots, we always return FALSE
2649 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2650 * reasons :) */
2651
2652 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2653 ? FALSE
2654 : mData->mCurrentStateModified;
2655
2656 return S_OK;
2657}
2658
2659STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2660{
2661 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2662
2663 AutoCaller autoCaller(this);
2664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2669 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2670
2671 return S_OK;
2672}
2673
2674STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2675{
2676 CheckComArgOutPointerValid(aClipboardMode);
2677
2678 AutoCaller autoCaller(this);
2679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2680
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 *aClipboardMode = mHWData->mClipboardMode;
2684
2685 return S_OK;
2686}
2687
2688STDMETHODIMP
2689Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2690{
2691 HRESULT rc = S_OK;
2692
2693 AutoCaller autoCaller(this);
2694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2695
2696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 alock.release();
2699 rc = onClipboardModeChange(aClipboardMode);
2700 alock.acquire();
2701 if (FAILED(rc)) return rc;
2702
2703 setModified(IsModified_MachineData);
2704 mHWData.backup();
2705 mHWData->mClipboardMode = aClipboardMode;
2706
2707 /* Save settings if online - todo why is this required?? */
2708 if (Global::IsOnline(mData->mMachineState))
2709 saveSettings(NULL);
2710
2711 return S_OK;
2712}
2713
2714STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2715{
2716 CheckComArgOutPointerValid(aDragAndDropMode);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aDragAndDropMode = mHWData->mDragAndDropMode;
2724
2725 return S_OK;
2726}
2727
2728STDMETHODIMP
2729Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2730{
2731 HRESULT rc = S_OK;
2732
2733 AutoCaller autoCaller(this);
2734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2735
2736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 alock.release();
2739 rc = onDragAndDropModeChange(aDragAndDropMode);
2740 alock.acquire();
2741 if (FAILED(rc)) return rc;
2742
2743 setModified(IsModified_MachineData);
2744 mHWData.backup();
2745 mHWData->mDragAndDropMode = aDragAndDropMode;
2746
2747 /* Save settings if online - todo why is this required?? */
2748 if (Global::IsOnline(mData->mMachineState))
2749 saveSettings(NULL);
2750
2751 return S_OK;
2752}
2753
2754STDMETHODIMP
2755Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2756{
2757 CheckComArgOutPointerValid(aPatterns);
2758
2759 AutoCaller autoCaller(this);
2760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2761
2762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2763
2764 try
2765 {
2766 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2767 }
2768 catch (...)
2769 {
2770 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2771 }
2772
2773 return S_OK;
2774}
2775
2776STDMETHODIMP
2777Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2778{
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 HRESULT rc = checkStateDependency(MutableStateDep);
2785 if (FAILED(rc)) return rc;
2786
2787 setModified(IsModified_MachineData);
2788 mHWData.backup();
2789 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2790 return rc;
2791}
2792
2793STDMETHODIMP
2794Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2795{
2796 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2797
2798 AutoCaller autoCaller(this);
2799 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2800
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2804 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2805
2806 return S_OK;
2807}
2808
2809STDMETHODIMP
2810Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2811{
2812 CheckComArgOutPointerValid(aEnabled);
2813
2814 AutoCaller autoCaller(this);
2815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2816
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aEnabled = mUserData->s.fTeleporterEnabled;
2820
2821 return S_OK;
2822}
2823
2824STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2825{
2826 AutoCaller autoCaller(this);
2827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2828
2829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 /* Only allow it to be set to true when PoweredOff or Aborted.
2832 (Clearing it is always permitted.) */
2833 if ( aEnabled
2834 && mData->mRegistered
2835 && ( !isSessionMachine()
2836 || ( mData->mMachineState != MachineState_PoweredOff
2837 && mData->mMachineState != MachineState_Teleported
2838 && mData->mMachineState != MachineState_Aborted
2839 )
2840 )
2841 )
2842 return setError(VBOX_E_INVALID_VM_STATE,
2843 tr("The machine is not powered off (state is %s)"),
2844 Global::stringifyMachineState(mData->mMachineState));
2845
2846 setModified(IsModified_MachineData);
2847 mUserData.backup();
2848 mUserData->s.fTeleporterEnabled = !!aEnabled;
2849
2850 return S_OK;
2851}
2852
2853STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2854{
2855 CheckComArgOutPointerValid(aPort);
2856
2857 AutoCaller autoCaller(this);
2858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2859
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2863
2864 return S_OK;
2865}
2866
2867STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2868{
2869 if (aPort >= _64K)
2870 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2871
2872 AutoCaller autoCaller(this);
2873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2874
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 HRESULT rc = checkStateDependency(MutableStateDep);
2878 if (FAILED(rc)) return rc;
2879
2880 setModified(IsModified_MachineData);
2881 mUserData.backup();
2882 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2883
2884 return S_OK;
2885}
2886
2887STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2888{
2889 CheckComArgOutPointerValid(aAddress);
2890
2891 AutoCaller autoCaller(this);
2892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2893
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2897
2898 return S_OK;
2899}
2900
2901STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2902{
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.strTeleporterAddress = aAddress;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2919{
2920 CheckComArgOutPointerValid(aPassword);
2921
2922 AutoCaller autoCaller(this);
2923 HRESULT hrc = autoCaller.rc();
2924 if (SUCCEEDED(hrc))
2925 {
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2928 }
2929
2930 return hrc;
2931}
2932
2933STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2934{
2935 /*
2936 * Hash the password first.
2937 */
2938 Utf8Str strPassword(aPassword);
2939 if (!strPassword.isEmpty())
2940 {
2941 if (VBoxIsPasswordHashed(&strPassword))
2942 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2943 VBoxHashPassword(&strPassword);
2944 }
2945
2946 /*
2947 * Do the update.
2948 */
2949 AutoCaller autoCaller(this);
2950 HRESULT hrc = autoCaller.rc();
2951 if (SUCCEEDED(hrc))
2952 {
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 hrc = checkStateDependency(MutableStateDep);
2955 if (SUCCEEDED(hrc))
2956 {
2957 setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterPassword = strPassword;
2960 }
2961 }
2962
2963 return hrc;
2964}
2965
2966STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2967{
2968 CheckComArgOutPointerValid(aState);
2969
2970 AutoCaller autoCaller(this);
2971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2972
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 *aState = mUserData->s.enmFaultToleranceState;
2976 return S_OK;
2977}
2978
2979STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2980{
2981 AutoCaller autoCaller(this);
2982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2983
2984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 /* @todo deal with running state change. */
2987 HRESULT rc = checkStateDependency(MutableStateDep);
2988 if (FAILED(rc)) return rc;
2989
2990 setModified(IsModified_MachineData);
2991 mUserData.backup();
2992 mUserData->s.enmFaultToleranceState = aState;
2993 return S_OK;
2994}
2995
2996STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2997{
2998 CheckComArgOutPointerValid(aAddress);
2999
3000 AutoCaller autoCaller(this);
3001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3002
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3010{
3011 AutoCaller autoCaller(this);
3012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3013
3014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 /* @todo deal with running state change. */
3017 HRESULT rc = checkStateDependency(MutableStateDep);
3018 if (FAILED(rc)) return rc;
3019
3020 setModified(IsModified_MachineData);
3021 mUserData.backup();
3022 mUserData->s.strFaultToleranceAddress = aAddress;
3023 return S_OK;
3024}
3025
3026STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3027{
3028 CheckComArgOutPointerValid(aPort);
3029
3030 AutoCaller autoCaller(this);
3031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3032
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 *aPort = mUserData->s.uFaultTolerancePort;
3036 return S_OK;
3037}
3038
3039STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3040{
3041 AutoCaller autoCaller(this);
3042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3043
3044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 /* @todo deal with running state change. */
3047 HRESULT rc = checkStateDependency(MutableStateDep);
3048 if (FAILED(rc)) return rc;
3049
3050 setModified(IsModified_MachineData);
3051 mUserData.backup();
3052 mUserData->s.uFaultTolerancePort = aPort;
3053 return S_OK;
3054}
3055
3056STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3057{
3058 CheckComArgOutPointerValid(aPassword);
3059
3060 AutoCaller autoCaller(this);
3061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3062
3063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3066
3067 return S_OK;
3068}
3069
3070STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3071{
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 /* @todo deal with running state change. */
3078 HRESULT rc = checkStateDependency(MutableStateDep);
3079 if (FAILED(rc)) return rc;
3080
3081 setModified(IsModified_MachineData);
3082 mUserData.backup();
3083 mUserData->s.strFaultTolerancePassword = aPassword;
3084
3085 return S_OK;
3086}
3087
3088STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3089{
3090 CheckComArgOutPointerValid(aInterval);
3091
3092 AutoCaller autoCaller(this);
3093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3094
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096
3097 *aInterval = mUserData->s.uFaultToleranceInterval;
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 /* @todo deal with running state change. */
3109 HRESULT rc = checkStateDependency(MutableStateDep);
3110 if (FAILED(rc)) return rc;
3111
3112 setModified(IsModified_MachineData);
3113 mUserData.backup();
3114 mUserData->s.uFaultToleranceInterval = aInterval;
3115 return S_OK;
3116}
3117
3118STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3119{
3120 CheckComArgOutPointerValid(aEnabled);
3121
3122 AutoCaller autoCaller(this);
3123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3124
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 *aEnabled = mUserData->s.fRTCUseUTC;
3128
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3133{
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* Only allow it to be set to true when PoweredOff or Aborted.
3140 (Clearing it is always permitted.) */
3141 if ( aEnabled
3142 && mData->mRegistered
3143 && ( !isSessionMachine()
3144 || ( mData->mMachineState != MachineState_PoweredOff
3145 && mData->mMachineState != MachineState_Teleported
3146 && mData->mMachineState != MachineState_Aborted
3147 )
3148 )
3149 )
3150 return setError(VBOX_E_INVALID_VM_STATE,
3151 tr("The machine is not powered off (state is %s)"),
3152 Global::stringifyMachineState(mData->mMachineState));
3153
3154 setModified(IsModified_MachineData);
3155 mUserData.backup();
3156 mUserData->s.fRTCUseUTC = !!aEnabled;
3157
3158 return S_OK;
3159}
3160
3161STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3162{
3163 CheckComArgOutPointerValid(aEnabled);
3164
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 *aEnabled = mHWData->mIOCacheEnabled;
3171
3172 return S_OK;
3173}
3174
3175STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3176{
3177 AutoCaller autoCaller(this);
3178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheEnabled = aEnabled;
3188
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3193{
3194 CheckComArgOutPointerValid(aIOCacheSize);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 *aIOCacheSize = mHWData->mIOCacheSize;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 HRESULT rc = checkStateDependency(MutableStateDep);
3214 if (FAILED(rc)) return rc;
3215
3216 setModified(IsModified_MachineData);
3217 mHWData.backup();
3218 mHWData->mIOCacheSize = aIOCacheSize;
3219
3220 return S_OK;
3221}
3222
3223
3224/**
3225 * @note Locks objects!
3226 */
3227STDMETHODIMP Machine::LockMachine(ISession *aSession,
3228 LockType_T lockType)
3229{
3230 CheckComArgNotNull(aSession);
3231
3232 AutoCaller autoCaller(this);
3233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3234
3235 /* check the session state */
3236 SessionState_T state;
3237 HRESULT rc = aSession->COMGETTER(State)(&state);
3238 if (FAILED(rc)) return rc;
3239
3240 if (state != SessionState_Unlocked)
3241 return setError(VBOX_E_INVALID_OBJECT_STATE,
3242 tr("The given session is busy"));
3243
3244 // get the client's IInternalSessionControl interface
3245 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3246 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3247 E_INVALIDARG);
3248
3249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 if (!mData->mRegistered)
3252 return setError(E_UNEXPECTED,
3253 tr("The machine '%s' is not registered"),
3254 mUserData->s.strName.c_str());
3255
3256 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3257
3258 SessionState_T oldState = mData->mSession.mState;
3259 /* Hack: in case the session is closing and there is a progress object
3260 * which allows waiting for the session to be closed, take the opportunity
3261 * and do a limited wait (max. 1 second). This helps a lot when the system
3262 * is busy and thus session closing can take a little while. */
3263 if ( mData->mSession.mState == SessionState_Unlocking
3264 && mData->mSession.mProgress)
3265 {
3266 alock.release();
3267 mData->mSession.mProgress->WaitForCompletion(1000);
3268 alock.acquire();
3269 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3270 }
3271
3272 // try again now
3273 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3274 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3275 )
3276 {
3277 // OK, share the session... we are now dealing with three processes:
3278 // 1) VBoxSVC (where this code runs);
3279 // 2) process C: the caller's client process (who wants a shared session);
3280 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3281
3282 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3283 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3284 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3285 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3286 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3287
3288 /*
3289 * Release the lock before calling the client process. It's safe here
3290 * since the only thing to do after we get the lock again is to add
3291 * the remote control to the list (which doesn't directly influence
3292 * anything).
3293 */
3294 alock.release();
3295
3296 // get the console of the session holding the write lock (this is a remote call)
3297 ComPtr<IConsole> pConsoleW;
3298 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3299 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3300 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3301 if (FAILED(rc))
3302 // the failure may occur w/o any error info (from RPC), so provide one
3303 return setError(VBOX_E_VM_ERROR,
3304 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3305
3306 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3307
3308 // share the session machine and W's console with the caller's session
3309 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3310 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3311 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3312
3313 if (FAILED(rc))
3314 // the failure may occur w/o any error info (from RPC), so provide one
3315 return setError(VBOX_E_VM_ERROR,
3316 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3317 alock.acquire();
3318
3319 // need to revalidate the state after acquiring the lock again
3320 if (mData->mSession.mState != SessionState_Locked)
3321 {
3322 pSessionControl->Uninitialize();
3323 return setError(VBOX_E_INVALID_SESSION_STATE,
3324 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3325 mUserData->s.strName.c_str());
3326 }
3327
3328 // add the caller's session to the list
3329 mData->mSession.mRemoteControls.push_back(pSessionControl);
3330 }
3331 else if ( mData->mSession.mState == SessionState_Locked
3332 || mData->mSession.mState == SessionState_Unlocking
3333 )
3334 {
3335 // sharing not permitted, or machine still unlocking:
3336 return setError(VBOX_E_INVALID_OBJECT_STATE,
3337 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3338 mUserData->s.strName.c_str());
3339 }
3340 else
3341 {
3342 // machine is not locked: then write-lock the machine (create the session machine)
3343
3344 // must not be busy
3345 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3346
3347 // get the caller's session PID
3348 RTPROCESS pid = NIL_RTPROCESS;
3349 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3350 pSessionControl->GetPID((ULONG*)&pid);
3351 Assert(pid != NIL_RTPROCESS);
3352
3353 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3354
3355 if (fLaunchingVMProcess)
3356 {
3357 // this machine is awaiting for a spawning session to be opened:
3358 // then the calling process must be the one that got started by
3359 // LaunchVMProcess()
3360
3361 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3362 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3363
3364 if (mData->mSession.mPID != pid)
3365 return setError(E_ACCESSDENIED,
3366 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3367 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3368 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3369 }
3370
3371 // create the mutable SessionMachine from the current machine
3372 ComObjPtr<SessionMachine> sessionMachine;
3373 sessionMachine.createObject();
3374 rc = sessionMachine->init(this);
3375 AssertComRC(rc);
3376
3377 /* NOTE: doing return from this function after this point but
3378 * before the end is forbidden since it may call SessionMachine::uninit()
3379 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3380 * lock while still holding the Machine lock in alock so that a deadlock
3381 * is possible due to the wrong lock order. */
3382
3383 if (SUCCEEDED(rc))
3384 {
3385 /*
3386 * Set the session state to Spawning to protect against subsequent
3387 * attempts to open a session and to unregister the machine after
3388 * we release the lock.
3389 */
3390 SessionState_T origState = mData->mSession.mState;
3391 mData->mSession.mState = SessionState_Spawning;
3392
3393 /*
3394 * Release the lock before calling the client process -- it will call
3395 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3396 * because the state is Spawning, so that LaunchVMProcess() and
3397 * LockMachine() calls will fail. This method, called before we
3398 * acquire the lock again, will fail because of the wrong PID.
3399 *
3400 * Note that mData->mSession.mRemoteControls accessed outside
3401 * the lock may not be modified when state is Spawning, so it's safe.
3402 */
3403 alock.release();
3404
3405 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3406 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3407 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3408
3409 /* The failure may occur w/o any error info (from RPC), so provide one */
3410 if (FAILED(rc))
3411 setError(VBOX_E_VM_ERROR,
3412 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3413
3414 if ( SUCCEEDED(rc)
3415 && fLaunchingVMProcess
3416 )
3417 {
3418 /* complete the remote session initialization */
3419
3420 /* get the console from the direct session */
3421 ComPtr<IConsole> console;
3422 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3423 ComAssertComRC(rc);
3424
3425 if (SUCCEEDED(rc) && !console)
3426 {
3427 ComAssert(!!console);
3428 rc = E_FAIL;
3429 }
3430
3431 /* assign machine & console to the remote session */
3432 if (SUCCEEDED(rc))
3433 {
3434 /*
3435 * after LaunchVMProcess(), the first and the only
3436 * entry in remoteControls is that remote session
3437 */
3438 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3439 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3440 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3441
3442 /* The failure may occur w/o any error info (from RPC), so provide one */
3443 if (FAILED(rc))
3444 setError(VBOX_E_VM_ERROR,
3445 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3446 }
3447
3448 if (FAILED(rc))
3449 pSessionControl->Uninitialize();
3450 }
3451
3452 /* acquire the lock again */
3453 alock.acquire();
3454
3455 /* Restore the session state */
3456 mData->mSession.mState = origState;
3457 }
3458
3459 // finalize spawning anyway (this is why we don't return on errors above)
3460 if (fLaunchingVMProcess)
3461 {
3462 /* Note that the progress object is finalized later */
3463 /** @todo Consider checking mData->mSession.mProgress for cancellation
3464 * around here. */
3465
3466 /* We don't reset mSession.mPID here because it is necessary for
3467 * SessionMachine::uninit() to reap the child process later. */
3468
3469 if (FAILED(rc))
3470 {
3471 /* Close the remote session, remove the remote control from the list
3472 * and reset session state to Closed (@note keep the code in sync
3473 * with the relevant part in openSession()). */
3474
3475 Assert(mData->mSession.mRemoteControls.size() == 1);
3476 if (mData->mSession.mRemoteControls.size() == 1)
3477 {
3478 ErrorInfoKeeper eik;
3479 mData->mSession.mRemoteControls.front()->Uninitialize();
3480 }
3481
3482 mData->mSession.mRemoteControls.clear();
3483 mData->mSession.mState = SessionState_Unlocked;
3484 }
3485 }
3486 else
3487 {
3488 /* memorize PID of the directly opened session */
3489 if (SUCCEEDED(rc))
3490 mData->mSession.mPID = pid;
3491 }
3492
3493 if (SUCCEEDED(rc))
3494 {
3495 /* memorize the direct session control and cache IUnknown for it */
3496 mData->mSession.mDirectControl = pSessionControl;
3497 mData->mSession.mState = SessionState_Locked;
3498 /* associate the SessionMachine with this Machine */
3499 mData->mSession.mMachine = sessionMachine;
3500
3501 /* request an IUnknown pointer early from the remote party for later
3502 * identity checks (it will be internally cached within mDirectControl
3503 * at least on XPCOM) */
3504 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3505 NOREF(unk);
3506 }
3507
3508 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3509 * would break the lock order */
3510 alock.release();
3511
3512 /* uninitialize the created session machine on failure */
3513 if (FAILED(rc))
3514 sessionMachine->uninit();
3515
3516 }
3517
3518 if (SUCCEEDED(rc))
3519 {
3520 /*
3521 * tell the client watcher thread to update the set of
3522 * machines that have open sessions
3523 */
3524 mParent->updateClientWatcher();
3525
3526 if (oldState != SessionState_Locked)
3527 /* fire an event */
3528 mParent->onSessionStateChange(getId(), SessionState_Locked);
3529 }
3530
3531 return rc;
3532}
3533
3534/**
3535 * @note Locks objects!
3536 */
3537STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3538 IN_BSTR aType,
3539 IN_BSTR aEnvironment,
3540 IProgress **aProgress)
3541{
3542 CheckComArgStrNotEmptyOrNull(aType);
3543 Utf8Str strType(aType);
3544 Utf8Str strEnvironment(aEnvironment);
3545 /* "emergencystop" doesn't need the session, so skip the checks/interface
3546 * retrieval. This code doesn't quite fit in here, but introducing a
3547 * special API method would be even more effort, and would require explicit
3548 * support by every API client. It's better to hide the feature a bit. */
3549 if (strType != "emergencystop")
3550 CheckComArgNotNull(aSession);
3551 CheckComArgOutPointerValid(aProgress);
3552
3553 AutoCaller autoCaller(this);
3554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3555
3556 ComPtr<IInternalSessionControl> control;
3557 HRESULT rc = S_OK;
3558
3559 if (strType != "emergencystop")
3560 {
3561 /* check the session state */
3562 SessionState_T state;
3563 rc = aSession->COMGETTER(State)(&state);
3564 if (FAILED(rc))
3565 return rc;
3566
3567 if (state != SessionState_Unlocked)
3568 return setError(VBOX_E_INVALID_OBJECT_STATE,
3569 tr("The given session is busy"));
3570
3571 /* get the IInternalSessionControl interface */
3572 control = aSession;
3573 ComAssertMsgRet(!control.isNull(),
3574 ("No IInternalSessionControl interface"),
3575 E_INVALIDARG);
3576 }
3577
3578 /* get the teleporter enable state for the progress object init. */
3579 BOOL fTeleporterEnabled;
3580 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3581 if (FAILED(rc))
3582 return rc;
3583
3584 /* create a progress object */
3585 if (strType != "emergencystop")
3586 {
3587 ComObjPtr<ProgressProxy> progress;
3588 progress.createObject();
3589 rc = progress->init(mParent,
3590 static_cast<IMachine*>(this),
3591 Bstr(tr("Starting VM")).raw(),
3592 TRUE /* aCancelable */,
3593 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3594 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3595 2 /* uFirstOperationWeight */,
3596 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3597
3598 if (SUCCEEDED(rc))
3599 {
3600 rc = launchVMProcess(control, strType, strEnvironment, progress);
3601 if (SUCCEEDED(rc))
3602 {
3603 progress.queryInterfaceTo(aProgress);
3604
3605 /* signal the client watcher thread */
3606 mParent->updateClientWatcher();
3607
3608 /* fire an event */
3609 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3610 }
3611 }
3612 }
3613 else
3614 {
3615 /* no progress object - either instant success or failure */
3616 *aProgress = NULL;
3617
3618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3619
3620 if (mData->mSession.mState != SessionState_Locked)
3621 return setError(VBOX_E_INVALID_OBJECT_STATE,
3622 tr("The machine '%s' is not locked by a session"),
3623 mUserData->s.strName.c_str());
3624
3625 /* must have a VM process associated - do not kill normal API clients
3626 * with an open session */
3627 if (!Global::IsOnline(mData->mMachineState))
3628 return setError(VBOX_E_INVALID_OBJECT_STATE,
3629 tr("The machine '%s' does not have a VM process"),
3630 mUserData->s.strName.c_str());
3631
3632 /* forcibly terminate the VM process */
3633 if (mData->mSession.mPID != NIL_RTPROCESS)
3634 RTProcTerminate(mData->mSession.mPID);
3635
3636 /* signal the client watcher thread, as most likely the client has
3637 * been terminated */
3638 mParent->updateClientWatcher();
3639 }
3640
3641 return rc;
3642}
3643
3644STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3645{
3646 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3647 return setError(E_INVALIDARG,
3648 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3649 aPosition, SchemaDefs::MaxBootPosition);
3650
3651 if (aDevice == DeviceType_USB)
3652 return setError(E_NOTIMPL,
3653 tr("Booting from USB device is currently not supported"));
3654
3655 AutoCaller autoCaller(this);
3656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3657
3658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3659
3660 HRESULT rc = checkStateDependency(MutableStateDep);
3661 if (FAILED(rc)) return rc;
3662
3663 setModified(IsModified_MachineData);
3664 mHWData.backup();
3665 mHWData->mBootOrder[aPosition - 1] = aDevice;
3666
3667 return S_OK;
3668}
3669
3670STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3671{
3672 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3673 return setError(E_INVALIDARG,
3674 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3675 aPosition, SchemaDefs::MaxBootPosition);
3676
3677 AutoCaller autoCaller(this);
3678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3679
3680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3681
3682 *aDevice = mHWData->mBootOrder[aPosition - 1];
3683
3684 return S_OK;
3685}
3686
3687STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3688 LONG aControllerPort,
3689 LONG aDevice,
3690 DeviceType_T aType,
3691 IMedium *aMedium)
3692{
3693 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3694 aControllerName, aControllerPort, aDevice, aType, aMedium));
3695
3696 CheckComArgStrNotEmptyOrNull(aControllerName);
3697
3698 AutoCaller autoCaller(this);
3699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3700
3701 // request the host lock first, since might be calling Host methods for getting host drives;
3702 // next, protect the media tree all the while we're in here, as well as our member variables
3703 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3704 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3705
3706 HRESULT rc = checkStateDependency(MutableStateDep);
3707 if (FAILED(rc)) return rc;
3708
3709 /// @todo NEWMEDIA implicit machine registration
3710 if (!mData->mRegistered)
3711 return setError(VBOX_E_INVALID_OBJECT_STATE,
3712 tr("Cannot attach storage devices to an unregistered machine"));
3713
3714 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3715
3716 /* Check for an existing controller. */
3717 ComObjPtr<StorageController> ctl;
3718 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3719 if (FAILED(rc)) return rc;
3720
3721 StorageControllerType_T ctrlType;
3722 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3723 if (FAILED(rc))
3724 return setError(E_FAIL,
3725 tr("Could not get type of controller '%ls'"),
3726 aControllerName);
3727
3728 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3729 bool fHotplug = false;
3730 if (Global::IsOnlineOrTransient(mData->mMachineState))
3731 fHotplug = true;
3732
3733 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3734 return setError(VBOX_E_INVALID_VM_STATE,
3735 tr("Controller '%ls' does not support hotplugging"),
3736 aControllerName);
3737
3738 // check that the port and device are not out of range
3739 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3740 if (FAILED(rc)) return rc;
3741
3742 /* check if the device slot is already busy */
3743 MediumAttachment *pAttachTemp;
3744 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3745 aControllerName,
3746 aControllerPort,
3747 aDevice)))
3748 {
3749 Medium *pMedium = pAttachTemp->getMedium();
3750 if (pMedium)
3751 {
3752 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3753 return setError(VBOX_E_OBJECT_IN_USE,
3754 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3755 pMedium->getLocationFull().c_str(),
3756 aControllerPort,
3757 aDevice,
3758 aControllerName);
3759 }
3760 else
3761 return setError(VBOX_E_OBJECT_IN_USE,
3762 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3763 aControllerPort, aDevice, aControllerName);
3764 }
3765
3766 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3767 if (aMedium && medium.isNull())
3768 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3769
3770 AutoCaller mediumCaller(medium);
3771 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3772
3773 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3774
3775 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3776 && !medium.isNull()
3777 )
3778 return setError(VBOX_E_OBJECT_IN_USE,
3779 tr("Medium '%s' is already attached to this virtual machine"),
3780 medium->getLocationFull().c_str());
3781
3782 if (!medium.isNull())
3783 {
3784 MediumType_T mtype = medium->getType();
3785 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3786 // For DVDs it's not written to the config file, so needs no global config
3787 // version bump. For floppies it's a new attribute "type", which is ignored
3788 // by older VirtualBox version, so needs no global config version bump either.
3789 // For hard disks this type is not accepted.
3790 if (mtype == MediumType_MultiAttach)
3791 {
3792 // This type is new with VirtualBox 4.0 and therefore requires settings
3793 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3794 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3795 // two reasons: The medium type is a property of the media registry tree, which
3796 // can reside in the global config file (for pre-4.0 media); we would therefore
3797 // possibly need to bump the global config version. We don't want to do that though
3798 // because that might make downgrading to pre-4.0 impossible.
3799 // As a result, we can only use these two new types if the medium is NOT in the
3800 // global registry:
3801 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3802 if ( medium->isInRegistry(uuidGlobalRegistry)
3803 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3804 )
3805 return setError(VBOX_E_INVALID_OBJECT_STATE,
3806 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3807 "to machines that were created with VirtualBox 4.0 or later"),
3808 medium->getLocationFull().c_str());
3809 }
3810 }
3811
3812 bool fIndirect = false;
3813 if (!medium.isNull())
3814 fIndirect = medium->isReadOnly();
3815 bool associate = true;
3816
3817 do
3818 {
3819 if ( aType == DeviceType_HardDisk
3820 && mMediaData.isBackedUp())
3821 {
3822 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3823
3824 /* check if the medium was attached to the VM before we started
3825 * changing attachments in which case the attachment just needs to
3826 * be restored */
3827 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3828 {
3829 AssertReturn(!fIndirect, E_FAIL);
3830
3831 /* see if it's the same bus/channel/device */
3832 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3833 {
3834 /* the simplest case: restore the whole attachment
3835 * and return, nothing else to do */
3836 mMediaData->mAttachments.push_back(pAttachTemp);
3837 return S_OK;
3838 }
3839
3840 /* bus/channel/device differ; we need a new attachment object,
3841 * but don't try to associate it again */
3842 associate = false;
3843 break;
3844 }
3845 }
3846
3847 /* go further only if the attachment is to be indirect */
3848 if (!fIndirect)
3849 break;
3850
3851 /* perform the so called smart attachment logic for indirect
3852 * attachments. Note that smart attachment is only applicable to base
3853 * hard disks. */
3854
3855 if (medium->getParent().isNull())
3856 {
3857 /* first, investigate the backup copy of the current hard disk
3858 * attachments to make it possible to re-attach existing diffs to
3859 * another device slot w/o losing their contents */
3860 if (mMediaData.isBackedUp())
3861 {
3862 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3863
3864 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3865 uint32_t foundLevel = 0;
3866
3867 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3868 it != oldAtts.end();
3869 ++it)
3870 {
3871 uint32_t level = 0;
3872 MediumAttachment *pAttach = *it;
3873 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3874 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3875 if (pMedium.isNull())
3876 continue;
3877
3878 if (pMedium->getBase(&level) == medium)
3879 {
3880 /* skip the hard disk if its currently attached (we
3881 * cannot attach the same hard disk twice) */
3882 if (findAttachment(mMediaData->mAttachments,
3883 pMedium))
3884 continue;
3885
3886 /* matched device, channel and bus (i.e. attached to the
3887 * same place) will win and immediately stop the search;
3888 * otherwise the attachment that has the youngest
3889 * descendant of medium will be used
3890 */
3891 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3892 {
3893 /* the simplest case: restore the whole attachment
3894 * and return, nothing else to do */
3895 mMediaData->mAttachments.push_back(*it);
3896 return S_OK;
3897 }
3898 else if ( foundIt == oldAtts.end()
3899 || level > foundLevel /* prefer younger */
3900 )
3901 {
3902 foundIt = it;
3903 foundLevel = level;
3904 }
3905 }
3906 }
3907
3908 if (foundIt != oldAtts.end())
3909 {
3910 /* use the previously attached hard disk */
3911 medium = (*foundIt)->getMedium();
3912 mediumCaller.attach(medium);
3913 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3914 mediumLock.attach(medium);
3915 /* not implicit, doesn't require association with this VM */
3916 fIndirect = false;
3917 associate = false;
3918 /* go right to the MediumAttachment creation */
3919 break;
3920 }
3921 }
3922
3923 /* must give up the medium lock and medium tree lock as below we
3924 * go over snapshots, which needs a lock with higher lock order. */
3925 mediumLock.release();
3926 treeLock.release();
3927
3928 /* then, search through snapshots for the best diff in the given
3929 * hard disk's chain to base the new diff on */
3930
3931 ComObjPtr<Medium> base;
3932 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3933 while (snap)
3934 {
3935 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3936
3937 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3938
3939 MediumAttachment *pAttachFound = NULL;
3940 uint32_t foundLevel = 0;
3941
3942 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3943 it != snapAtts.end();
3944 ++it)
3945 {
3946 MediumAttachment *pAttach = *it;
3947 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3948 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3949 if (pMedium.isNull())
3950 continue;
3951
3952 uint32_t level = 0;
3953 if (pMedium->getBase(&level) == medium)
3954 {
3955 /* matched device, channel and bus (i.e. attached to the
3956 * same place) will win and immediately stop the search;
3957 * otherwise the attachment that has the youngest
3958 * descendant of medium will be used
3959 */
3960 if ( pAttach->getDevice() == aDevice
3961 && pAttach->getPort() == aControllerPort
3962 && pAttach->getControllerName() == aControllerName
3963 )
3964 {
3965 pAttachFound = pAttach;
3966 break;
3967 }
3968 else if ( !pAttachFound
3969 || level > foundLevel /* prefer younger */
3970 )
3971 {
3972 pAttachFound = pAttach;
3973 foundLevel = level;
3974 }
3975 }
3976 }
3977
3978 if (pAttachFound)
3979 {
3980 base = pAttachFound->getMedium();
3981 break;
3982 }
3983
3984 snap = snap->getParent();
3985 }
3986
3987 /* re-lock medium tree and the medium, as we need it below */
3988 treeLock.acquire();
3989 mediumLock.acquire();
3990
3991 /* found a suitable diff, use it as a base */
3992 if (!base.isNull())
3993 {
3994 medium = base;
3995 mediumCaller.attach(medium);
3996 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3997 mediumLock.attach(medium);
3998 }
3999 }
4000
4001 Utf8Str strFullSnapshotFolder;
4002 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4003
4004 ComObjPtr<Medium> diff;
4005 diff.createObject();
4006 // store this diff in the same registry as the parent
4007 Guid uuidRegistryParent;
4008 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4009 {
4010 // parent image has no registry: this can happen if we're attaching a new immutable
4011 // image that has not yet been attached (medium then points to the base and we're
4012 // creating the diff image for the immutable, and the parent is not yet registered);
4013 // put the parent in the machine registry then
4014 mediumLock.release();
4015 treeLock.release();
4016 alock.release();
4017 addMediumToRegistry(medium);
4018 alock.acquire();
4019 treeLock.acquire();
4020 mediumLock.acquire();
4021 medium->getFirstRegistryMachineId(uuidRegistryParent);
4022 }
4023 rc = diff->init(mParent,
4024 medium->getPreferredDiffFormat(),
4025 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4026 uuidRegistryParent);
4027 if (FAILED(rc)) return rc;
4028
4029 /* Apply the normal locking logic to the entire chain. */
4030 MediumLockList *pMediumLockList(new MediumLockList());
4031 mediumLock.release();
4032 treeLock.release();
4033 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4034 true /* fMediumLockWrite */,
4035 medium,
4036 *pMediumLockList);
4037 treeLock.acquire();
4038 mediumLock.acquire();
4039 if (SUCCEEDED(rc))
4040 {
4041 mediumLock.release();
4042 treeLock.release();
4043 rc = pMediumLockList->Lock();
4044 treeLock.acquire();
4045 mediumLock.acquire();
4046 if (FAILED(rc))
4047 setError(rc,
4048 tr("Could not lock medium when creating diff '%s'"),
4049 diff->getLocationFull().c_str());
4050 else
4051 {
4052 /* will release the lock before the potentially lengthy
4053 * operation, so protect with the special state */
4054 MachineState_T oldState = mData->mMachineState;
4055 setMachineState(MachineState_SettingUp);
4056
4057 mediumLock.release();
4058 treeLock.release();
4059 alock.release();
4060
4061 rc = medium->createDiffStorage(diff,
4062 MediumVariant_Standard,
4063 pMediumLockList,
4064 NULL /* aProgress */,
4065 true /* aWait */);
4066
4067 alock.acquire();
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070
4071 setMachineState(oldState);
4072 }
4073 }
4074
4075 /* Unlock the media and free the associated memory. */
4076 delete pMediumLockList;
4077
4078 if (FAILED(rc)) return rc;
4079
4080 /* use the created diff for the actual attachment */
4081 medium = diff;
4082 mediumCaller.attach(medium);
4083 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4084 mediumLock.attach(medium);
4085 }
4086 while (0);
4087
4088 ComObjPtr<MediumAttachment> attachment;
4089 attachment.createObject();
4090 rc = attachment->init(this,
4091 medium,
4092 aControllerName,
4093 aControllerPort,
4094 aDevice,
4095 aType,
4096 fIndirect,
4097 false /* fPassthrough */,
4098 false /* fTempEject */,
4099 false /* fNonRotational */,
4100 false /* fDiscard */,
4101 Utf8Str::Empty);
4102 if (FAILED(rc)) return rc;
4103
4104 if (associate && !medium.isNull())
4105 {
4106 // as the last step, associate the medium to the VM
4107 rc = medium->addBackReference(mData->mUuid);
4108 // here we can fail because of Deleting, or being in process of creating a Diff
4109 if (FAILED(rc)) return rc;
4110
4111 mediumLock.release();
4112 treeLock.release();
4113 alock.release();
4114 addMediumToRegistry(medium);
4115 alock.acquire();
4116 treeLock.acquire();
4117 mediumLock.acquire();
4118 }
4119
4120 /* success: finally remember the attachment */
4121 setModified(IsModified_Storage);
4122 mMediaData.backup();
4123 mMediaData->mAttachments.push_back(attachment);
4124
4125 mediumLock.release();
4126 treeLock.release();
4127 alock.release();
4128
4129 if (fHotplug)
4130 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4131
4132 mParent->saveModifiedRegistries();
4133
4134 return rc;
4135}
4136
4137STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4138 LONG aDevice)
4139{
4140 CheckComArgStrNotEmptyOrNull(aControllerName);
4141
4142 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4143 aControllerName, aControllerPort, aDevice));
4144
4145 AutoCaller autoCaller(this);
4146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4147
4148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4149
4150 HRESULT rc = checkStateDependency(MutableStateDep);
4151 if (FAILED(rc)) return rc;
4152
4153 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4154
4155 /* Check for an existing controller. */
4156 ComObjPtr<StorageController> ctl;
4157 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4158 if (FAILED(rc)) return rc;
4159
4160 StorageControllerType_T ctrlType;
4161 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4162 if (FAILED(rc))
4163 return setError(E_FAIL,
4164 tr("Could not get type of controller '%ls'"),
4165 aControllerName);
4166
4167 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4168 bool fHotplug = false;
4169 if (Global::IsOnlineOrTransient(mData->mMachineState))
4170 fHotplug = true;
4171
4172 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4173 return setError(VBOX_E_INVALID_VM_STATE,
4174 tr("Controller '%ls' does not support hotplugging"),
4175 aControllerName);
4176
4177 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4178 aControllerName,
4179 aControllerPort,
4180 aDevice);
4181 if (!pAttach)
4182 return setError(VBOX_E_OBJECT_NOT_FOUND,
4183 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4184 aDevice, aControllerPort, aControllerName);
4185
4186 /*
4187 * The VM has to detach the device before we delete any implicit diffs.
4188 * If this fails we can roll back without loosing data.
4189 */
4190 if (fHotplug)
4191 {
4192 alock.release();
4193 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4194 alock.acquire();
4195 }
4196 if (FAILED(rc)) return rc;
4197
4198 /* If we are here everything went well and we can delete the implicit now. */
4199 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4200
4201 alock.release();
4202
4203 mParent->saveModifiedRegistries();
4204
4205 return rc;
4206}
4207
4208STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4209 LONG aDevice, BOOL aPassthrough)
4210{
4211 CheckComArgStrNotEmptyOrNull(aControllerName);
4212
4213 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4214 aControllerName, aControllerPort, aDevice, aPassthrough));
4215
4216 AutoCaller autoCaller(this);
4217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4218
4219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4220
4221 HRESULT rc = checkStateDependency(MutableStateDep);
4222 if (FAILED(rc)) return rc;
4223
4224 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4225
4226 if (Global::IsOnlineOrTransient(mData->mMachineState))
4227 return setError(VBOX_E_INVALID_VM_STATE,
4228 tr("Invalid machine state: %s"),
4229 Global::stringifyMachineState(mData->mMachineState));
4230
4231 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4232 aControllerName,
4233 aControllerPort,
4234 aDevice);
4235 if (!pAttach)
4236 return setError(VBOX_E_OBJECT_NOT_FOUND,
4237 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4238 aDevice, aControllerPort, aControllerName);
4239
4240
4241 setModified(IsModified_Storage);
4242 mMediaData.backup();
4243
4244 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4245
4246 if (pAttach->getType() != DeviceType_DVD)
4247 return setError(E_INVALIDARG,
4248 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4249 aDevice, aControllerPort, aControllerName);
4250 pAttach->updatePassthrough(!!aPassthrough);
4251
4252 return S_OK;
4253}
4254
4255STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4256 LONG aDevice, BOOL aTemporaryEject)
4257{
4258 CheckComArgStrNotEmptyOrNull(aControllerName);
4259
4260 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4261 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4262
4263 AutoCaller autoCaller(this);
4264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4265
4266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4267
4268 HRESULT rc = checkStateDependency(MutableStateDep);
4269 if (FAILED(rc)) return rc;
4270
4271 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4272 aControllerName,
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4278 aDevice, aControllerPort, aControllerName);
4279
4280
4281 setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4285
4286 if (pAttach->getType() != DeviceType_DVD)
4287 return setError(E_INVALIDARG,
4288 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4289 aDevice, aControllerPort, aControllerName);
4290 pAttach->updateTempEject(!!aTemporaryEject);
4291
4292 return S_OK;
4293}
4294
4295STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4296 LONG aDevice, BOOL aNonRotational)
4297{
4298 CheckComArgStrNotEmptyOrNull(aControllerName);
4299
4300 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4301 aControllerName, aControllerPort, aDevice, aNonRotational));
4302
4303 AutoCaller autoCaller(this);
4304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4305
4306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4307
4308 HRESULT rc = checkStateDependency(MutableStateDep);
4309 if (FAILED(rc)) return rc;
4310
4311 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4312
4313 if (Global::IsOnlineOrTransient(mData->mMachineState))
4314 return setError(VBOX_E_INVALID_VM_STATE,
4315 tr("Invalid machine state: %s"),
4316 Global::stringifyMachineState(mData->mMachineState));
4317
4318 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4319 aControllerName,
4320 aControllerPort,
4321 aDevice);
4322 if (!pAttach)
4323 return setError(VBOX_E_OBJECT_NOT_FOUND,
4324 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4325 aDevice, aControllerPort, aControllerName);
4326
4327
4328 setModified(IsModified_Storage);
4329 mMediaData.backup();
4330
4331 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4332
4333 if (pAttach->getType() != DeviceType_HardDisk)
4334 return setError(E_INVALIDARG,
4335 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"),
4336 aDevice, aControllerPort, aControllerName);
4337 pAttach->updateNonRotational(!!aNonRotational);
4338
4339 return S_OK;
4340}
4341
4342STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4343 LONG aDevice, BOOL aDiscard)
4344{
4345 CheckComArgStrNotEmptyOrNull(aControllerName);
4346
4347 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4348 aControllerName, aControllerPort, aDevice, aDiscard));
4349
4350 AutoCaller autoCaller(this);
4351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4352
4353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4354
4355 HRESULT rc = checkStateDependency(MutableStateDep);
4356 if (FAILED(rc)) return rc;
4357
4358 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4359
4360 if (Global::IsOnlineOrTransient(mData->mMachineState))
4361 return setError(VBOX_E_INVALID_VM_STATE,
4362 tr("Invalid machine state: %s"),
4363 Global::stringifyMachineState(mData->mMachineState));
4364
4365 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4366 aControllerName,
4367 aControllerPort,
4368 aDevice);
4369 if (!pAttach)
4370 return setError(VBOX_E_OBJECT_NOT_FOUND,
4371 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4372 aDevice, aControllerPort, aControllerName);
4373
4374
4375 setModified(IsModified_Storage);
4376 mMediaData.backup();
4377
4378 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4379
4380 if (pAttach->getType() != DeviceType_HardDisk)
4381 return setError(E_INVALIDARG,
4382 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"),
4383 aDevice, aControllerPort, aControllerName);
4384 pAttach->updateDiscard(!!aDiscard);
4385
4386 return S_OK;
4387}
4388
4389STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4390 LONG aDevice)
4391{
4392 int rc = S_OK;
4393 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4394 aControllerName, aControllerPort, aDevice));
4395
4396 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4397
4398 return rc;
4399}
4400
4401STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4402 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4403{
4404 CheckComArgStrNotEmptyOrNull(aControllerName);
4405
4406 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4407 aControllerName, aControllerPort, aDevice));
4408
4409 AutoCaller autoCaller(this);
4410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4411
4412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4413
4414 HRESULT rc = checkStateDependency(MutableStateDep);
4415 if (FAILED(rc)) return rc;
4416
4417 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4418
4419 if (Global::IsOnlineOrTransient(mData->mMachineState))
4420 return setError(VBOX_E_INVALID_VM_STATE,
4421 tr("Invalid machine state: %s"),
4422 Global::stringifyMachineState(mData->mMachineState));
4423
4424 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4425 aControllerName,
4426 aControllerPort,
4427 aDevice);
4428 if (!pAttach)
4429 return setError(VBOX_E_OBJECT_NOT_FOUND,
4430 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4431 aDevice, aControllerPort, aControllerName);
4432
4433
4434 setModified(IsModified_Storage);
4435 mMediaData.backup();
4436
4437 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4438 if (aBandwidthGroup && group.isNull())
4439 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4440
4441 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4442
4443 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4444 if (strBandwidthGroupOld.isNotEmpty())
4445 {
4446 /* Get the bandwidth group object and release it - this must not fail. */
4447 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4448 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4449 Assert(SUCCEEDED(rc));
4450
4451 pBandwidthGroupOld->release();
4452 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4453 }
4454
4455 if (!group.isNull())
4456 {
4457 group->reference();
4458 pAttach->updateBandwidthGroup(group->getName());
4459 }
4460
4461 return S_OK;
4462}
4463
4464STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4465 LONG aControllerPort,
4466 LONG aDevice,
4467 DeviceType_T aType)
4468{
4469 HRESULT rc = S_OK;
4470
4471 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4472 aControllerName, aControllerPort, aDevice, aType));
4473
4474 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4475
4476 return rc;
4477}
4478
4479
4480
4481STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 BOOL aForce)
4485{
4486 int rc = S_OK;
4487 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4488 aControllerName, aControllerPort, aForce));
4489
4490 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4491
4492 return rc;
4493}
4494
4495STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4496 LONG aControllerPort,
4497 LONG aDevice,
4498 IMedium *aMedium,
4499 BOOL aForce)
4500{
4501 int rc = S_OK;
4502 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4503 aControllerName, aControllerPort, aDevice, aForce));
4504
4505 CheckComArgStrNotEmptyOrNull(aControllerName);
4506
4507 AutoCaller autoCaller(this);
4508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4509
4510 // request the host lock first, since might be calling Host methods for getting host drives;
4511 // next, protect the media tree all the while we're in here, as well as our member variables
4512 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4513 this->lockHandle(),
4514 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4515
4516 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4517 aControllerName,
4518 aControllerPort,
4519 aDevice);
4520 if (pAttach.isNull())
4521 return setError(VBOX_E_OBJECT_NOT_FOUND,
4522 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4523 aDevice, aControllerPort, aControllerName);
4524
4525 /* Remember previously mounted medium. The medium before taking the
4526 * backup is not necessarily the same thing. */
4527 ComObjPtr<Medium> oldmedium;
4528 oldmedium = pAttach->getMedium();
4529
4530 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4531 if (aMedium && pMedium.isNull())
4532 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4533
4534 AutoCaller mediumCaller(pMedium);
4535 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4536
4537 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4538 if (pMedium)
4539 {
4540 DeviceType_T mediumType = pAttach->getType();
4541 switch (mediumType)
4542 {
4543 case DeviceType_DVD:
4544 case DeviceType_Floppy:
4545 break;
4546
4547 default:
4548 return setError(VBOX_E_INVALID_OBJECT_STATE,
4549 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4550 aControllerPort,
4551 aDevice,
4552 aControllerName);
4553 }
4554 }
4555
4556 setModified(IsModified_Storage);
4557 mMediaData.backup();
4558
4559 {
4560 // The backup operation makes the pAttach reference point to the
4561 // old settings. Re-get the correct reference.
4562 pAttach = findAttachment(mMediaData->mAttachments,
4563 aControllerName,
4564 aControllerPort,
4565 aDevice);
4566 if (!oldmedium.isNull())
4567 oldmedium->removeBackReference(mData->mUuid);
4568 if (!pMedium.isNull())
4569 {
4570 pMedium->addBackReference(mData->mUuid);
4571
4572 mediumLock.release();
4573 multiLock.release();
4574 addMediumToRegistry(pMedium);
4575 multiLock.acquire();
4576 mediumLock.acquire();
4577 }
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580 pAttach->updateMedium(pMedium);
4581 }
4582
4583 setModified(IsModified_Storage);
4584
4585 mediumLock.release();
4586 multiLock.release();
4587 rc = onMediumChange(pAttach, aForce);
4588 multiLock.acquire();
4589 mediumLock.acquire();
4590
4591 /* On error roll back this change only. */
4592 if (FAILED(rc))
4593 {
4594 if (!pMedium.isNull())
4595 pMedium->removeBackReference(mData->mUuid);
4596 pAttach = findAttachment(mMediaData->mAttachments,
4597 aControllerName,
4598 aControllerPort,
4599 aDevice);
4600 /* If the attachment is gone in the meantime, bail out. */
4601 if (pAttach.isNull())
4602 return rc;
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604 if (!oldmedium.isNull())
4605 oldmedium->addBackReference(mData->mUuid);
4606 pAttach->updateMedium(oldmedium);
4607 }
4608
4609 mediumLock.release();
4610 multiLock.release();
4611
4612 mParent->saveModifiedRegistries();
4613
4614 return rc;
4615}
4616
4617STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4618 LONG aControllerPort,
4619 LONG aDevice,
4620 IMedium **aMedium)
4621{
4622 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4623 aControllerName, aControllerPort, aDevice));
4624
4625 CheckComArgStrNotEmptyOrNull(aControllerName);
4626 CheckComArgOutPointerValid(aMedium);
4627
4628 AutoCaller autoCaller(this);
4629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 *aMedium = NULL;
4634
4635 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4636 aControllerName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4642 aDevice, aControllerPort, aControllerName);
4643
4644 pAttach->getMedium().queryInterfaceTo(aMedium);
4645
4646 return S_OK;
4647}
4648
4649STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4650{
4651 CheckComArgOutPointerValid(port);
4652 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4653
4654 AutoCaller autoCaller(this);
4655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4656
4657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4658
4659 mSerialPorts[slot].queryInterfaceTo(port);
4660
4661 return S_OK;
4662}
4663
4664STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4665{
4666 CheckComArgOutPointerValid(port);
4667 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4668
4669 AutoCaller autoCaller(this);
4670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4671
4672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4673
4674 mParallelPorts[slot].queryInterfaceTo(port);
4675
4676 return S_OK;
4677}
4678
4679STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4680{
4681 CheckComArgOutPointerValid(adapter);
4682 /* Do not assert if slot is out of range, just return the advertised
4683 status. testdriver/vbox.py triggers this in logVmInfo. */
4684 if (slot >= mNetworkAdapters.size())
4685 return setError(E_INVALIDARG,
4686 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4687 slot, mNetworkAdapters.size());
4688
4689 AutoCaller autoCaller(this);
4690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4691
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4695
4696 return S_OK;
4697}
4698
4699STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4700{
4701 CheckComArgOutSafeArrayPointerValid(aKeys);
4702
4703 AutoCaller autoCaller(this);
4704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4705
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4709 int i = 0;
4710 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4711 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4712 ++it, ++i)
4713 {
4714 const Utf8Str &strKey = it->first;
4715 strKey.cloneTo(&saKeys[i]);
4716 }
4717 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4718
4719 return S_OK;
4720 }
4721
4722 /**
4723 * @note Locks this object for reading.
4724 */
4725STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4726 BSTR *aValue)
4727{
4728 CheckComArgStrNotEmptyOrNull(aKey);
4729 CheckComArgOutPointerValid(aValue);
4730
4731 AutoCaller autoCaller(this);
4732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4733
4734 /* start with nothing found */
4735 Bstr bstrResult("");
4736
4737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4738
4739 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4740 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4741 // found:
4742 bstrResult = it->second; // source is a Utf8Str
4743
4744 /* return the result to caller (may be empty) */
4745 bstrResult.cloneTo(aValue);
4746
4747 return S_OK;
4748}
4749
4750 /**
4751 * @note Locks mParent for writing + this object for writing.
4752 */
4753STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4754{
4755 CheckComArgStrNotEmptyOrNull(aKey);
4756
4757 AutoCaller autoCaller(this);
4758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4759
4760 Utf8Str strKey(aKey);
4761 Utf8Str strValue(aValue);
4762 Utf8Str strOldValue; // empty
4763
4764 // locking note: we only hold the read lock briefly to look up the old value,
4765 // then release it and call the onExtraCanChange callbacks. There is a small
4766 // chance of a race insofar as the callback might be called twice if two callers
4767 // change the same key at the same time, but that's a much better solution
4768 // than the deadlock we had here before. The actual changing of the extradata
4769 // is then performed under the write lock and race-free.
4770
4771 // look up the old value first; if nothing has changed then we need not do anything
4772 {
4773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4774 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4775 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4776 strOldValue = it->second;
4777 }
4778
4779 bool fChanged;
4780 if ((fChanged = (strOldValue != strValue)))
4781 {
4782 // ask for permission from all listeners outside the locks;
4783 // onExtraDataCanChange() only briefly requests the VirtualBox
4784 // lock to copy the list of callbacks to invoke
4785 Bstr error;
4786 Bstr bstrValue(aValue);
4787
4788 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4789 {
4790 const char *sep = error.isEmpty() ? "" : ": ";
4791 CBSTR err = error.raw();
4792 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4793 sep, err));
4794 return setError(E_ACCESSDENIED,
4795 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4796 aKey,
4797 bstrValue.raw(),
4798 sep,
4799 err);
4800 }
4801
4802 // data is changing and change not vetoed: then write it out under the lock
4803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 if (isSnapshotMachine())
4806 {
4807 HRESULT rc = checkStateDependency(MutableStateDep);
4808 if (FAILED(rc)) return rc;
4809 }
4810
4811 if (strValue.isEmpty())
4812 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4813 else
4814 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4815 // creates a new key if needed
4816
4817 bool fNeedsGlobalSaveSettings = false;
4818 saveSettings(&fNeedsGlobalSaveSettings);
4819
4820 if (fNeedsGlobalSaveSettings)
4821 {
4822 // save the global settings; for that we should hold only the VirtualBox lock
4823 alock.release();
4824 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4825 mParent->saveSettings();
4826 }
4827 }
4828
4829 // fire notification outside the lock
4830 if (fChanged)
4831 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4832
4833 return S_OK;
4834}
4835
4836STDMETHODIMP Machine::SaveSettings()
4837{
4838 AutoCaller autoCaller(this);
4839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4840
4841 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4842
4843 /* when there was auto-conversion, we want to save the file even if
4844 * the VM is saved */
4845 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4846 if (FAILED(rc)) return rc;
4847
4848 /* the settings file path may never be null */
4849 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4850
4851 /* save all VM data excluding snapshots */
4852 bool fNeedsGlobalSaveSettings = false;
4853 rc = saveSettings(&fNeedsGlobalSaveSettings);
4854 mlock.release();
4855
4856 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4857 {
4858 // save the global settings; for that we should hold only the VirtualBox lock
4859 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4860 rc = mParent->saveSettings();
4861 }
4862
4863 return rc;
4864}
4865
4866STDMETHODIMP Machine::DiscardSettings()
4867{
4868 AutoCaller autoCaller(this);
4869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4870
4871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4872
4873 HRESULT rc = checkStateDependency(MutableStateDep);
4874 if (FAILED(rc)) return rc;
4875
4876 /*
4877 * during this rollback, the session will be notified if data has
4878 * been actually changed
4879 */
4880 rollback(true /* aNotify */);
4881
4882 return S_OK;
4883}
4884
4885/** @note Locks objects! */
4886STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4887 ComSafeArrayOut(IMedium*, aMedia))
4888{
4889 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4890 AutoLimitedCaller autoCaller(this);
4891 AssertComRCReturnRC(autoCaller.rc());
4892
4893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 Guid id(getId());
4896
4897 if (mData->mSession.mState != SessionState_Unlocked)
4898 return setError(VBOX_E_INVALID_OBJECT_STATE,
4899 tr("Cannot unregister the machine '%s' while it is locked"),
4900 mUserData->s.strName.c_str());
4901
4902 // wait for state dependents to drop to zero
4903 ensureNoStateDependencies();
4904
4905 if (!mData->mAccessible)
4906 {
4907 // inaccessible maschines can only be unregistered; uninitialize ourselves
4908 // here because currently there may be no unregistered that are inaccessible
4909 // (this state combination is not supported). Note releasing the caller and
4910 // leaving the lock before calling uninit()
4911 alock.release();
4912 autoCaller.release();
4913
4914 uninit();
4915
4916 mParent->unregisterMachine(this, id);
4917 // calls VirtualBox::saveSettings()
4918
4919 return S_OK;
4920 }
4921
4922 HRESULT rc = S_OK;
4923
4924 // discard saved state
4925 if (mData->mMachineState == MachineState_Saved)
4926 {
4927 // add the saved state file to the list of files the caller should delete
4928 Assert(!mSSData->strStateFilePath.isEmpty());
4929 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4930
4931 mSSData->strStateFilePath.setNull();
4932
4933 // unconditionally set the machine state to powered off, we now
4934 // know no session has locked the machine
4935 mData->mMachineState = MachineState_PoweredOff;
4936 }
4937
4938 size_t cSnapshots = 0;
4939 if (mData->mFirstSnapshot)
4940 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4941 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4942 // fail now before we start detaching media
4943 return setError(VBOX_E_INVALID_OBJECT_STATE,
4944 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4945 mUserData->s.strName.c_str(), cSnapshots);
4946
4947 // This list collects the medium objects from all medium attachments
4948 // which we will detach from the machine and its snapshots, in a specific
4949 // order which allows for closing all media without getting "media in use"
4950 // errors, simply by going through the list from the front to the back:
4951 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4952 // and must be closed before the parent media from the snapshots, or closing the parents
4953 // will fail because they still have children);
4954 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4955 // the root ("first") snapshot of the machine.
4956 MediaList llMedia;
4957
4958 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4959 && mMediaData->mAttachments.size()
4960 )
4961 {
4962 // we have media attachments: detach them all and add the Medium objects to our list
4963 if (cleanupMode != CleanupMode_UnregisterOnly)
4964 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4965 else
4966 return setError(VBOX_E_INVALID_OBJECT_STATE,
4967 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4968 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4969 }
4970
4971 if (cSnapshots)
4972 {
4973 // autoCleanup must be true here, or we would have failed above
4974
4975 // add the media from the medium attachments of the snapshots to llMedia
4976 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4977 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4978 // into the children first
4979
4980 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4981 MachineState_T oldState = mData->mMachineState;
4982 mData->mMachineState = MachineState_DeletingSnapshot;
4983
4984 // make a copy of the first snapshot so the refcount does not drop to 0
4985 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4986 // because of the AutoCaller voodoo)
4987 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4988
4989 // GO!
4990 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4991
4992 mData->mMachineState = oldState;
4993 }
4994
4995 if (FAILED(rc))
4996 {
4997 rollbackMedia();
4998 return rc;
4999 }
5000
5001 // commit all the media changes made above
5002 commitMedia();
5003
5004 mData->mRegistered = false;
5005
5006 // machine lock no longer needed
5007 alock.release();
5008
5009 // return media to caller
5010 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5011 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5012
5013 mParent->unregisterMachine(this, id);
5014 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5015
5016 return S_OK;
5017}
5018
5019struct Machine::DeleteTask
5020{
5021 ComObjPtr<Machine> pMachine;
5022 RTCList<ComPtr<IMedium> > llMediums;
5023 StringsList llFilesToDelete;
5024 ComObjPtr<Progress> pProgress;
5025};
5026
5027STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5028{
5029 LogFlowFuncEnter();
5030
5031 AutoCaller autoCaller(this);
5032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5033
5034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 HRESULT rc = checkStateDependency(MutableStateDep);
5037 if (FAILED(rc)) return rc;
5038
5039 if (mData->mRegistered)
5040 return setError(VBOX_E_INVALID_VM_STATE,
5041 tr("Cannot delete settings of a registered machine"));
5042
5043 DeleteTask *pTask = new DeleteTask;
5044 pTask->pMachine = this;
5045 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5046
5047 // collect files to delete
5048 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5049
5050 for (size_t i = 0; i < sfaMedia.size(); ++i)
5051 {
5052 IMedium *pIMedium(sfaMedia[i]);
5053 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5054 if (pMedium.isNull())
5055 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5056 SafeArray<BSTR> ids;
5057 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5058 if (FAILED(rc)) return rc;
5059 /* At this point the medium should not have any back references
5060 * anymore. If it has it is attached to another VM and *must* not
5061 * deleted. */
5062 if (ids.size() < 1)
5063 pTask->llMediums.append(pMedium);
5064 }
5065 if (mData->pMachineConfigFile->fileExists())
5066 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5067
5068 pTask->pProgress.createObject();
5069 pTask->pProgress->init(getVirtualBox(),
5070 static_cast<IMachine*>(this) /* aInitiator */,
5071 Bstr(tr("Deleting files")).raw(),
5072 true /* fCancellable */,
5073 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5074 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5075
5076 int vrc = RTThreadCreate(NULL,
5077 Machine::deleteThread,
5078 (void*)pTask,
5079 0,
5080 RTTHREADTYPE_MAIN_WORKER,
5081 0,
5082 "MachineDelete");
5083
5084 pTask->pProgress.queryInterfaceTo(aProgress);
5085
5086 if (RT_FAILURE(vrc))
5087 {
5088 delete pTask;
5089 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5090 }
5091
5092 LogFlowFuncLeave();
5093
5094 return S_OK;
5095}
5096
5097/**
5098 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5099 * calls Machine::deleteTaskWorker() on the actual machine object.
5100 * @param Thread
5101 * @param pvUser
5102 * @return
5103 */
5104/*static*/
5105DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5106{
5107 LogFlowFuncEnter();
5108
5109 DeleteTask *pTask = (DeleteTask*)pvUser;
5110 Assert(pTask);
5111 Assert(pTask->pMachine);
5112 Assert(pTask->pProgress);
5113
5114 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5115 pTask->pProgress->notifyComplete(rc);
5116
5117 delete pTask;
5118
5119 LogFlowFuncLeave();
5120
5121 NOREF(Thread);
5122
5123 return VINF_SUCCESS;
5124}
5125
5126/**
5127 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5128 * @param task
5129 * @return
5130 */
5131HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5132{
5133 AutoCaller autoCaller(this);
5134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5135
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 HRESULT rc = S_OK;
5139
5140 try
5141 {
5142 ULONG uLogHistoryCount = 3;
5143 ComPtr<ISystemProperties> systemProperties;
5144 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5145 if (FAILED(rc)) throw rc;
5146
5147 if (!systemProperties.isNull())
5148 {
5149 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5150 if (FAILED(rc)) throw rc;
5151 }
5152
5153 MachineState_T oldState = mData->mMachineState;
5154 setMachineState(MachineState_SettingUp);
5155 alock.release();
5156 for (size_t i = 0; i < task.llMediums.size(); ++i)
5157 {
5158 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5159 {
5160 AutoCaller mac(pMedium);
5161 if (FAILED(mac.rc())) throw mac.rc();
5162 Utf8Str strLocation = pMedium->getLocationFull();
5163 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5164 if (FAILED(rc)) throw rc;
5165 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5166 }
5167 ComPtr<IProgress> pProgress2;
5168 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5169 if (FAILED(rc)) throw rc;
5170 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5171 if (FAILED(rc)) throw rc;
5172 /* Check the result of the asynchrony process. */
5173 LONG iRc;
5174 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5175 if (FAILED(rc)) throw rc;
5176 /* If the thread of the progress object has an error, then
5177 * retrieve the error info from there, or it'll be lost. */
5178 if (FAILED(iRc))
5179 throw setError(ProgressErrorInfo(pProgress2));
5180 }
5181 setMachineState(oldState);
5182 alock.acquire();
5183
5184 // delete the files pushed on the task list by Machine::Delete()
5185 // (this includes saved states of the machine and snapshots and
5186 // medium storage files from the IMedium list passed in, and the
5187 // machine XML file)
5188 StringsList::const_iterator it = task.llFilesToDelete.begin();
5189 while (it != task.llFilesToDelete.end())
5190 {
5191 const Utf8Str &strFile = *it;
5192 LogFunc(("Deleting file %s\n", strFile.c_str()));
5193 int vrc = RTFileDelete(strFile.c_str());
5194 if (RT_FAILURE(vrc))
5195 throw setError(VBOX_E_IPRT_ERROR,
5196 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5197
5198 ++it;
5199 if (it == task.llFilesToDelete.end())
5200 {
5201 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5202 if (FAILED(rc)) throw rc;
5203 break;
5204 }
5205
5206 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5207 if (FAILED(rc)) throw rc;
5208 }
5209
5210 /* delete the settings only when the file actually exists */
5211 if (mData->pMachineConfigFile->fileExists())
5212 {
5213 /* Delete any backup or uncommitted XML files. Ignore failures.
5214 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5215 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5216 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5217 RTFileDelete(otherXml.c_str());
5218 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5219 RTFileDelete(otherXml.c_str());
5220
5221 /* delete the Logs folder, nothing important should be left
5222 * there (we don't check for errors because the user might have
5223 * some private files there that we don't want to delete) */
5224 Utf8Str logFolder;
5225 getLogFolder(logFolder);
5226 Assert(logFolder.length());
5227 if (RTDirExists(logFolder.c_str()))
5228 {
5229 /* Delete all VBox.log[.N] files from the Logs folder
5230 * (this must be in sync with the rotation logic in
5231 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5232 * files that may have been created by the GUI. */
5233 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5234 logFolder.c_str(), RTPATH_DELIMITER);
5235 RTFileDelete(log.c_str());
5236 log = Utf8StrFmt("%s%cVBox.png",
5237 logFolder.c_str(), RTPATH_DELIMITER);
5238 RTFileDelete(log.c_str());
5239 for (int i = uLogHistoryCount; i > 0; i--)
5240 {
5241 log = Utf8StrFmt("%s%cVBox.log.%d",
5242 logFolder.c_str(), RTPATH_DELIMITER, i);
5243 RTFileDelete(log.c_str());
5244 log = Utf8StrFmt("%s%cVBox.png.%d",
5245 logFolder.c_str(), RTPATH_DELIMITER, i);
5246 RTFileDelete(log.c_str());
5247 }
5248
5249 RTDirRemove(logFolder.c_str());
5250 }
5251
5252 /* delete the Snapshots folder, nothing important should be left
5253 * there (we don't check for errors because the user might have
5254 * some private files there that we don't want to delete) */
5255 Utf8Str strFullSnapshotFolder;
5256 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5257 Assert(!strFullSnapshotFolder.isEmpty());
5258 if (RTDirExists(strFullSnapshotFolder.c_str()))
5259 RTDirRemove(strFullSnapshotFolder.c_str());
5260
5261 // delete the directory that contains the settings file, but only
5262 // if it matches the VM name
5263 Utf8Str settingsDir;
5264 if (isInOwnDir(&settingsDir))
5265 RTDirRemove(settingsDir.c_str());
5266 }
5267
5268 alock.release();
5269
5270 mParent->saveModifiedRegistries();
5271 }
5272 catch (HRESULT aRC) { rc = aRC; }
5273
5274 return rc;
5275}
5276
5277STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5278{
5279 CheckComArgOutPointerValid(aSnapshot);
5280
5281 AutoCaller autoCaller(this);
5282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5283
5284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5285
5286 ComObjPtr<Snapshot> pSnapshot;
5287 HRESULT rc;
5288
5289 if (!aNameOrId || !*aNameOrId)
5290 // null case (caller wants root snapshot): findSnapshotById() handles this
5291 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5292 else
5293 {
5294 Guid uuid(aNameOrId);
5295 if (!uuid.isEmpty())
5296 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5297 else
5298 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5299 }
5300 pSnapshot.queryInterfaceTo(aSnapshot);
5301
5302 return rc;
5303}
5304
5305STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5306{
5307 CheckComArgStrNotEmptyOrNull(aName);
5308 CheckComArgStrNotEmptyOrNull(aHostPath);
5309
5310 AutoCaller autoCaller(this);
5311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5312
5313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5314
5315 HRESULT rc = checkStateDependency(MutableStateDep);
5316 if (FAILED(rc)) return rc;
5317
5318 Utf8Str strName(aName);
5319
5320 ComObjPtr<SharedFolder> sharedFolder;
5321 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5322 if (SUCCEEDED(rc))
5323 return setError(VBOX_E_OBJECT_IN_USE,
5324 tr("Shared folder named '%s' already exists"),
5325 strName.c_str());
5326
5327 sharedFolder.createObject();
5328 rc = sharedFolder->init(getMachine(),
5329 strName,
5330 aHostPath,
5331 !!aWritable,
5332 !!aAutoMount,
5333 true /* fFailOnError */);
5334 if (FAILED(rc)) return rc;
5335
5336 setModified(IsModified_SharedFolders);
5337 mHWData.backup();
5338 mHWData->mSharedFolders.push_back(sharedFolder);
5339
5340 /* inform the direct session if any */
5341 alock.release();
5342 onSharedFolderChange();
5343
5344 return S_OK;
5345}
5346
5347STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5348{
5349 CheckComArgStrNotEmptyOrNull(aName);
5350
5351 AutoCaller autoCaller(this);
5352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5353
5354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5355
5356 HRESULT rc = checkStateDependency(MutableStateDep);
5357 if (FAILED(rc)) return rc;
5358
5359 ComObjPtr<SharedFolder> sharedFolder;
5360 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5361 if (FAILED(rc)) return rc;
5362
5363 setModified(IsModified_SharedFolders);
5364 mHWData.backup();
5365 mHWData->mSharedFolders.remove(sharedFolder);
5366
5367 /* inform the direct session if any */
5368 alock.release();
5369 onSharedFolderChange();
5370
5371 return S_OK;
5372}
5373
5374STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5375{
5376 CheckComArgOutPointerValid(aCanShow);
5377
5378 /* start with No */
5379 *aCanShow = FALSE;
5380
5381 AutoCaller autoCaller(this);
5382 AssertComRCReturnRC(autoCaller.rc());
5383
5384 ComPtr<IInternalSessionControl> directControl;
5385 {
5386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 if (mData->mSession.mState != SessionState_Locked)
5389 return setError(VBOX_E_INVALID_VM_STATE,
5390 tr("Machine is not locked for session (session state: %s)"),
5391 Global::stringifySessionState(mData->mSession.mState));
5392
5393 directControl = mData->mSession.mDirectControl;
5394 }
5395
5396 /* ignore calls made after #OnSessionEnd() is called */
5397 if (!directControl)
5398 return S_OK;
5399
5400 LONG64 dummy;
5401 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5402}
5403
5404STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5405{
5406 CheckComArgOutPointerValid(aWinId);
5407
5408 AutoCaller autoCaller(this);
5409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5410
5411 ComPtr<IInternalSessionControl> directControl;
5412 {
5413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5414
5415 if (mData->mSession.mState != SessionState_Locked)
5416 return setError(E_FAIL,
5417 tr("Machine is not locked for session (session state: %s)"),
5418 Global::stringifySessionState(mData->mSession.mState));
5419
5420 directControl = mData->mSession.mDirectControl;
5421 }
5422
5423 /* ignore calls made after #OnSessionEnd() is called */
5424 if (!directControl)
5425 return S_OK;
5426
5427 BOOL dummy;
5428 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5429}
5430
5431#ifdef VBOX_WITH_GUEST_PROPS
5432/**
5433 * Look up a guest property in VBoxSVC's internal structures.
5434 */
5435HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5436 BSTR *aValue,
5437 LONG64 *aTimestamp,
5438 BSTR *aFlags) const
5439{
5440 using namespace guestProp;
5441
5442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5443 Utf8Str strName(aName);
5444 HWData::GuestPropertyList::const_iterator it;
5445
5446 for (it = mHWData->mGuestProperties.begin();
5447 it != mHWData->mGuestProperties.end(); ++it)
5448 {
5449 if (it->strName == strName)
5450 {
5451 char szFlags[MAX_FLAGS_LEN + 1];
5452 it->strValue.cloneTo(aValue);
5453 *aTimestamp = it->mTimestamp;
5454 writeFlags(it->mFlags, szFlags);
5455 Bstr(szFlags).cloneTo(aFlags);
5456 break;
5457 }
5458 }
5459 return S_OK;
5460}
5461
5462/**
5463 * Query the VM that a guest property belongs to for the property.
5464 * @returns E_ACCESSDENIED if the VM process is not available or not
5465 * currently handling queries and the lookup should then be done in
5466 * VBoxSVC.
5467 */
5468HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5469 BSTR *aValue,
5470 LONG64 *aTimestamp,
5471 BSTR *aFlags) const
5472{
5473 HRESULT rc;
5474 ComPtr<IInternalSessionControl> directControl;
5475 directControl = mData->mSession.mDirectControl;
5476
5477 /* fail if we were called after #OnSessionEnd() is called. This is a
5478 * silly race condition. */
5479
5480 if (!directControl)
5481 rc = E_ACCESSDENIED;
5482 else
5483 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5484 false /* isSetter */,
5485 aValue, aTimestamp, aFlags);
5486 return rc;
5487}
5488#endif // VBOX_WITH_GUEST_PROPS
5489
5490STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5491 BSTR *aValue,
5492 LONG64 *aTimestamp,
5493 BSTR *aFlags)
5494{
5495#ifndef VBOX_WITH_GUEST_PROPS
5496 ReturnComNotImplemented();
5497#else // VBOX_WITH_GUEST_PROPS
5498 CheckComArgStrNotEmptyOrNull(aName);
5499 CheckComArgOutPointerValid(aValue);
5500 CheckComArgOutPointerValid(aTimestamp);
5501 CheckComArgOutPointerValid(aFlags);
5502
5503 AutoCaller autoCaller(this);
5504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5505
5506 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5507 if (rc == E_ACCESSDENIED)
5508 /* The VM is not running or the service is not (yet) accessible */
5509 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5510 return rc;
5511#endif // VBOX_WITH_GUEST_PROPS
5512}
5513
5514STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5515{
5516 LONG64 dummyTimestamp;
5517 Bstr dummyFlags;
5518 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5519}
5520
5521STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5522{
5523 Bstr dummyValue;
5524 Bstr dummyFlags;
5525 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5526}
5527
5528#ifdef VBOX_WITH_GUEST_PROPS
5529/**
5530 * Set a guest property in VBoxSVC's internal structures.
5531 */
5532HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5533 IN_BSTR aFlags)
5534{
5535 using namespace guestProp;
5536
5537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5538 HRESULT rc = S_OK;
5539 HWData::GuestProperty property;
5540 property.mFlags = NILFLAG;
5541 bool found = false;
5542
5543 rc = checkStateDependency(MutableStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 try
5547 {
5548 Utf8Str utf8Name(aName);
5549 Utf8Str utf8Flags(aFlags);
5550 uint32_t fFlags = NILFLAG;
5551 if ( (aFlags != NULL)
5552 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5553 )
5554 return setError(E_INVALIDARG,
5555 tr("Invalid flag values: '%ls'"),
5556 aFlags);
5557
5558 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5559 * know, this is simple and do an OK job atm.) */
5560 HWData::GuestPropertyList::iterator it;
5561 for (it = mHWData->mGuestProperties.begin();
5562 it != mHWData->mGuestProperties.end(); ++it)
5563 if (it->strName == utf8Name)
5564 {
5565 property = *it;
5566 if (it->mFlags & (RDONLYHOST))
5567 rc = setError(E_ACCESSDENIED,
5568 tr("The property '%ls' cannot be changed by the host"),
5569 aName);
5570 else
5571 {
5572 setModified(IsModified_MachineData);
5573 mHWData.backup(); // @todo r=dj backup in a loop?!?
5574
5575 /* The backup() operation invalidates our iterator, so
5576 * get a new one. */
5577 for (it = mHWData->mGuestProperties.begin();
5578 it->strName != utf8Name;
5579 ++it)
5580 ;
5581 mHWData->mGuestProperties.erase(it);
5582 }
5583 found = true;
5584 break;
5585 }
5586 if (found && SUCCEEDED(rc))
5587 {
5588 if (aValue)
5589 {
5590 RTTIMESPEC time;
5591 property.strValue = aValue;
5592 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5593 if (aFlags != NULL)
5594 property.mFlags = fFlags;
5595 mHWData->mGuestProperties.push_back(property);
5596 }
5597 }
5598 else if (SUCCEEDED(rc) && aValue)
5599 {
5600 RTTIMESPEC time;
5601 setModified(IsModified_MachineData);
5602 mHWData.backup();
5603 property.strName = aName;
5604 property.strValue = aValue;
5605 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5606 property.mFlags = fFlags;
5607 mHWData->mGuestProperties.push_back(property);
5608 }
5609 if ( SUCCEEDED(rc)
5610 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5611 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5612 RTSTR_MAX,
5613 utf8Name.c_str(),
5614 RTSTR_MAX,
5615 NULL)
5616 )
5617 )
5618 {
5619 /** @todo r=bird: Why aren't we leaving the lock here? The
5620 * same code in PushGuestProperty does... */
5621 mParent->onGuestPropertyChange(mData->mUuid, aName,
5622 aValue ? aValue : Bstr("").raw(),
5623 aFlags ? aFlags : Bstr("").raw());
5624 }
5625 }
5626 catch (std::bad_alloc &)
5627 {
5628 rc = E_OUTOFMEMORY;
5629 }
5630
5631 return rc;
5632}
5633
5634/**
5635 * Set a property on the VM that that property belongs to.
5636 * @returns E_ACCESSDENIED if the VM process is not available or not
5637 * currently handling queries and the setting should then be done in
5638 * VBoxSVC.
5639 */
5640HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5641 IN_BSTR aFlags)
5642{
5643 HRESULT rc;
5644
5645 try
5646 {
5647 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5648
5649 BSTR dummy = NULL; /* will not be changed (setter) */
5650 LONG64 dummy64;
5651 if (!directControl)
5652 rc = E_ACCESSDENIED;
5653 else
5654 /** @todo Fix when adding DeleteGuestProperty(),
5655 see defect. */
5656 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5657 true /* isSetter */,
5658 &dummy, &dummy64, &dummy);
5659 }
5660 catch (std::bad_alloc &)
5661 {
5662 rc = E_OUTOFMEMORY;
5663 }
5664
5665 return rc;
5666}
5667#endif // VBOX_WITH_GUEST_PROPS
5668
5669STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5670 IN_BSTR aFlags)
5671{
5672#ifndef VBOX_WITH_GUEST_PROPS
5673 ReturnComNotImplemented();
5674#else // VBOX_WITH_GUEST_PROPS
5675 CheckComArgStrNotEmptyOrNull(aName);
5676 CheckComArgMaybeNull(aFlags);
5677 CheckComArgMaybeNull(aValue);
5678
5679 AutoCaller autoCaller(this);
5680 if (FAILED(autoCaller.rc()))
5681 return autoCaller.rc();
5682
5683 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5684 if (rc == E_ACCESSDENIED)
5685 /* The VM is not running or the service is not (yet) accessible */
5686 rc = setGuestPropertyToService(aName, aValue, aFlags);
5687 return rc;
5688#endif // VBOX_WITH_GUEST_PROPS
5689}
5690
5691STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5692{
5693 return SetGuestProperty(aName, aValue, NULL);
5694}
5695
5696STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5697{
5698 return SetGuestProperty(aName, NULL, NULL);
5699}
5700
5701#ifdef VBOX_WITH_GUEST_PROPS
5702/**
5703 * Enumerate the guest properties in VBoxSVC's internal structures.
5704 */
5705HRESULT Machine::enumerateGuestPropertiesInService
5706 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5707 ComSafeArrayOut(BSTR, aValues),
5708 ComSafeArrayOut(LONG64, aTimestamps),
5709 ComSafeArrayOut(BSTR, aFlags))
5710{
5711 using namespace guestProp;
5712
5713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5714 Utf8Str strPatterns(aPatterns);
5715
5716 /*
5717 * Look for matching patterns and build up a list.
5718 */
5719 HWData::GuestPropertyList propList;
5720 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5721 it != mHWData->mGuestProperties.end();
5722 ++it)
5723 if ( strPatterns.isEmpty()
5724 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5725 RTSTR_MAX,
5726 it->strName.c_str(),
5727 RTSTR_MAX,
5728 NULL)
5729 )
5730 propList.push_back(*it);
5731
5732 /*
5733 * And build up the arrays for returning the property information.
5734 */
5735 size_t cEntries = propList.size();
5736 SafeArray<BSTR> names(cEntries);
5737 SafeArray<BSTR> values(cEntries);
5738 SafeArray<LONG64> timestamps(cEntries);
5739 SafeArray<BSTR> flags(cEntries);
5740 size_t iProp = 0;
5741 for (HWData::GuestPropertyList::iterator it = propList.begin();
5742 it != propList.end();
5743 ++it)
5744 {
5745 char szFlags[MAX_FLAGS_LEN + 1];
5746 it->strName.cloneTo(&names[iProp]);
5747 it->strValue.cloneTo(&values[iProp]);
5748 timestamps[iProp] = it->mTimestamp;
5749 writeFlags(it->mFlags, szFlags);
5750 Bstr(szFlags).cloneTo(&flags[iProp]);
5751 ++iProp;
5752 }
5753 names.detachTo(ComSafeArrayOutArg(aNames));
5754 values.detachTo(ComSafeArrayOutArg(aValues));
5755 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5756 flags.detachTo(ComSafeArrayOutArg(aFlags));
5757 return S_OK;
5758}
5759
5760/**
5761 * Enumerate the properties managed by a VM.
5762 * @returns E_ACCESSDENIED if the VM process is not available or not
5763 * currently handling queries and the setting should then be done in
5764 * VBoxSVC.
5765 */
5766HRESULT Machine::enumerateGuestPropertiesOnVM
5767 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5768 ComSafeArrayOut(BSTR, aValues),
5769 ComSafeArrayOut(LONG64, aTimestamps),
5770 ComSafeArrayOut(BSTR, aFlags))
5771{
5772 HRESULT rc;
5773 ComPtr<IInternalSessionControl> directControl;
5774 directControl = mData->mSession.mDirectControl;
5775
5776 if (!directControl)
5777 rc = E_ACCESSDENIED;
5778 else
5779 rc = directControl->EnumerateGuestProperties
5780 (aPatterns, ComSafeArrayOutArg(aNames),
5781 ComSafeArrayOutArg(aValues),
5782 ComSafeArrayOutArg(aTimestamps),
5783 ComSafeArrayOutArg(aFlags));
5784 return rc;
5785}
5786#endif // VBOX_WITH_GUEST_PROPS
5787
5788STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5789 ComSafeArrayOut(BSTR, aNames),
5790 ComSafeArrayOut(BSTR, aValues),
5791 ComSafeArrayOut(LONG64, aTimestamps),
5792 ComSafeArrayOut(BSTR, aFlags))
5793{
5794#ifndef VBOX_WITH_GUEST_PROPS
5795 ReturnComNotImplemented();
5796#else // VBOX_WITH_GUEST_PROPS
5797 CheckComArgMaybeNull(aPatterns);
5798 CheckComArgOutSafeArrayPointerValid(aNames);
5799 CheckComArgOutSafeArrayPointerValid(aValues);
5800 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5801 CheckComArgOutSafeArrayPointerValid(aFlags);
5802
5803 AutoCaller autoCaller(this);
5804 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5805
5806 HRESULT rc = enumerateGuestPropertiesOnVM
5807 (aPatterns, ComSafeArrayOutArg(aNames),
5808 ComSafeArrayOutArg(aValues),
5809 ComSafeArrayOutArg(aTimestamps),
5810 ComSafeArrayOutArg(aFlags));
5811 if (rc == E_ACCESSDENIED)
5812 /* The VM is not running or the service is not (yet) accessible */
5813 rc = enumerateGuestPropertiesInService
5814 (aPatterns, ComSafeArrayOutArg(aNames),
5815 ComSafeArrayOutArg(aValues),
5816 ComSafeArrayOutArg(aTimestamps),
5817 ComSafeArrayOutArg(aFlags));
5818 return rc;
5819#endif // VBOX_WITH_GUEST_PROPS
5820}
5821
5822STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5823 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5824{
5825 MediaData::AttachmentList atts;
5826
5827 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5828 if (FAILED(rc)) return rc;
5829
5830 SafeIfaceArray<IMediumAttachment> attachments(atts);
5831 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5832
5833 return S_OK;
5834}
5835
5836STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5837 LONG aControllerPort,
5838 LONG aDevice,
5839 IMediumAttachment **aAttachment)
5840{
5841 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5842 aControllerName, aControllerPort, aDevice));
5843
5844 CheckComArgStrNotEmptyOrNull(aControllerName);
5845 CheckComArgOutPointerValid(aAttachment);
5846
5847 AutoCaller autoCaller(this);
5848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5849
5850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5851
5852 *aAttachment = NULL;
5853
5854 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5855 aControllerName,
5856 aControllerPort,
5857 aDevice);
5858 if (pAttach.isNull())
5859 return setError(VBOX_E_OBJECT_NOT_FOUND,
5860 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5861 aDevice, aControllerPort, aControllerName);
5862
5863 pAttach.queryInterfaceTo(aAttachment);
5864
5865 return S_OK;
5866}
5867
5868STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5869 StorageBus_T aConnectionType,
5870 IStorageController **controller)
5871{
5872 CheckComArgStrNotEmptyOrNull(aName);
5873
5874 if ( (aConnectionType <= StorageBus_Null)
5875 || (aConnectionType > StorageBus_SAS))
5876 return setError(E_INVALIDARG,
5877 tr("Invalid connection type: %d"),
5878 aConnectionType);
5879
5880 AutoCaller autoCaller(this);
5881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5882
5883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5884
5885 HRESULT rc = checkStateDependency(MutableStateDep);
5886 if (FAILED(rc)) return rc;
5887
5888 /* try to find one with the name first. */
5889 ComObjPtr<StorageController> ctrl;
5890
5891 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5892 if (SUCCEEDED(rc))
5893 return setError(VBOX_E_OBJECT_IN_USE,
5894 tr("Storage controller named '%ls' already exists"),
5895 aName);
5896
5897 ctrl.createObject();
5898
5899 /* get a new instance number for the storage controller */
5900 ULONG ulInstance = 0;
5901 bool fBootable = true;
5902 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5903 it != mStorageControllers->end();
5904 ++it)
5905 {
5906 if ((*it)->getStorageBus() == aConnectionType)
5907 {
5908 ULONG ulCurInst = (*it)->getInstance();
5909
5910 if (ulCurInst >= ulInstance)
5911 ulInstance = ulCurInst + 1;
5912
5913 /* Only one controller of each type can be marked as bootable. */
5914 if ((*it)->getBootable())
5915 fBootable = false;
5916 }
5917 }
5918
5919 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5920 if (FAILED(rc)) return rc;
5921
5922 setModified(IsModified_Storage);
5923 mStorageControllers.backup();
5924 mStorageControllers->push_back(ctrl);
5925
5926 ctrl.queryInterfaceTo(controller);
5927
5928 /* inform the direct session if any */
5929 alock.release();
5930 onStorageControllerChange();
5931
5932 return S_OK;
5933}
5934
5935STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5936 IStorageController **aStorageController)
5937{
5938 CheckComArgStrNotEmptyOrNull(aName);
5939
5940 AutoCaller autoCaller(this);
5941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5942
5943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5944
5945 ComObjPtr<StorageController> ctrl;
5946
5947 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5948 if (SUCCEEDED(rc))
5949 ctrl.queryInterfaceTo(aStorageController);
5950
5951 return rc;
5952}
5953
5954STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5955 IStorageController **aStorageController)
5956{
5957 AutoCaller autoCaller(this);
5958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5959
5960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5961
5962 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5963 it != mStorageControllers->end();
5964 ++it)
5965 {
5966 if ((*it)->getInstance() == aInstance)
5967 {
5968 (*it).queryInterfaceTo(aStorageController);
5969 return S_OK;
5970 }
5971 }
5972
5973 return setError(VBOX_E_OBJECT_NOT_FOUND,
5974 tr("Could not find a storage controller with instance number '%lu'"),
5975 aInstance);
5976}
5977
5978STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5979{
5980 AutoCaller autoCaller(this);
5981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5982
5983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5984
5985 HRESULT rc = checkStateDependency(MutableStateDep);
5986 if (FAILED(rc)) return rc;
5987
5988 ComObjPtr<StorageController> ctrl;
5989
5990 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5991 if (SUCCEEDED(rc))
5992 {
5993 /* Ensure that only one controller of each type is marked as bootable. */
5994 if (fBootable == TRUE)
5995 {
5996 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5997 it != mStorageControllers->end();
5998 ++it)
5999 {
6000 ComObjPtr<StorageController> aCtrl = (*it);
6001
6002 if ( (aCtrl->getName() != Utf8Str(aName))
6003 && aCtrl->getBootable() == TRUE
6004 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6005 && aCtrl->getControllerType() == ctrl->getControllerType())
6006 {
6007 aCtrl->setBootable(FALSE);
6008 break;
6009 }
6010 }
6011 }
6012
6013 if (SUCCEEDED(rc))
6014 {
6015 ctrl->setBootable(fBootable);
6016 setModified(IsModified_Storage);
6017 }
6018 }
6019
6020 if (SUCCEEDED(rc))
6021 {
6022 /* inform the direct session if any */
6023 alock.release();
6024 onStorageControllerChange();
6025 }
6026
6027 return rc;
6028}
6029
6030STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6031{
6032 CheckComArgStrNotEmptyOrNull(aName);
6033
6034 AutoCaller autoCaller(this);
6035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6036
6037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 HRESULT rc = checkStateDependency(MutableStateDep);
6040 if (FAILED(rc)) return rc;
6041
6042 ComObjPtr<StorageController> ctrl;
6043 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6044 if (FAILED(rc)) return rc;
6045
6046 {
6047 /* find all attached devices to the appropriate storage controller and detach them all */
6048 // make a temporary list because detachDevice invalidates iterators into
6049 // mMediaData->mAttachments
6050 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6051
6052 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6053 it != llAttachments2.end();
6054 ++it)
6055 {
6056 MediumAttachment *pAttachTemp = *it;
6057
6058 AutoCaller localAutoCaller(pAttachTemp);
6059 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6060
6061 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6062
6063 if (pAttachTemp->getControllerName() == aName)
6064 {
6065 rc = detachDevice(pAttachTemp, alock, NULL);
6066 if (FAILED(rc)) return rc;
6067 }
6068 }
6069 }
6070
6071 /* We can remove it now. */
6072 setModified(IsModified_Storage);
6073 mStorageControllers.backup();
6074
6075 ctrl->unshare();
6076
6077 mStorageControllers->remove(ctrl);
6078
6079 /* inform the direct session if any */
6080 alock.release();
6081 onStorageControllerChange();
6082
6083 return S_OK;
6084}
6085
6086STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6087 ULONG *puOriginX,
6088 ULONG *puOriginY,
6089 ULONG *puWidth,
6090 ULONG *puHeight,
6091 BOOL *pfEnabled)
6092{
6093 LogFlowThisFunc(("\n"));
6094
6095 CheckComArgNotNull(puOriginX);
6096 CheckComArgNotNull(puOriginY);
6097 CheckComArgNotNull(puWidth);
6098 CheckComArgNotNull(puHeight);
6099 CheckComArgNotNull(pfEnabled);
6100
6101 uint32_t u32OriginX= 0;
6102 uint32_t u32OriginY= 0;
6103 uint32_t u32Width = 0;
6104 uint32_t u32Height = 0;
6105 uint16_t u16Flags = 0;
6106
6107 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6108 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6109 if (RT_FAILURE(vrc))
6110 {
6111#ifdef RT_OS_WINDOWS
6112 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6113 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6114 * So just assign fEnable to TRUE again.
6115 * The right fix would be to change GUI API wrappers to make sure that parameters
6116 * are changed only if API succeeds.
6117 */
6118 *pfEnabled = TRUE;
6119#endif
6120 return setError(VBOX_E_IPRT_ERROR,
6121 tr("Saved guest size is not available (%Rrc)"),
6122 vrc);
6123 }
6124
6125 *puOriginX = u32OriginX;
6126 *puOriginY = u32OriginY;
6127 *puWidth = u32Width;
6128 *puHeight = u32Height;
6129 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6130
6131 return S_OK;
6132}
6133
6134STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6135{
6136 LogFlowThisFunc(("\n"));
6137
6138 CheckComArgNotNull(aSize);
6139 CheckComArgNotNull(aWidth);
6140 CheckComArgNotNull(aHeight);
6141
6142 if (aScreenId != 0)
6143 return E_NOTIMPL;
6144
6145 AutoCaller autoCaller(this);
6146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6147
6148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 uint8_t *pu8Data = NULL;
6151 uint32_t cbData = 0;
6152 uint32_t u32Width = 0;
6153 uint32_t u32Height = 0;
6154
6155 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6156
6157 if (RT_FAILURE(vrc))
6158 return setError(VBOX_E_IPRT_ERROR,
6159 tr("Saved screenshot data is not available (%Rrc)"),
6160 vrc);
6161
6162 *aSize = cbData;
6163 *aWidth = u32Width;
6164 *aHeight = u32Height;
6165
6166 freeSavedDisplayScreenshot(pu8Data);
6167
6168 return S_OK;
6169}
6170
6171STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6172{
6173 LogFlowThisFunc(("\n"));
6174
6175 CheckComArgNotNull(aWidth);
6176 CheckComArgNotNull(aHeight);
6177 CheckComArgOutSafeArrayPointerValid(aData);
6178
6179 if (aScreenId != 0)
6180 return E_NOTIMPL;
6181
6182 AutoCaller autoCaller(this);
6183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6184
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 uint8_t *pu8Data = NULL;
6188 uint32_t cbData = 0;
6189 uint32_t u32Width = 0;
6190 uint32_t u32Height = 0;
6191
6192 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6193
6194 if (RT_FAILURE(vrc))
6195 return setError(VBOX_E_IPRT_ERROR,
6196 tr("Saved screenshot data is not available (%Rrc)"),
6197 vrc);
6198
6199 *aWidth = u32Width;
6200 *aHeight = u32Height;
6201
6202 com::SafeArray<BYTE> bitmap(cbData);
6203 /* Convert pixels to format expected by the API caller. */
6204 if (aBGR)
6205 {
6206 /* [0] B, [1] G, [2] R, [3] A. */
6207 for (unsigned i = 0; i < cbData; i += 4)
6208 {
6209 bitmap[i] = pu8Data[i];
6210 bitmap[i + 1] = pu8Data[i + 1];
6211 bitmap[i + 2] = pu8Data[i + 2];
6212 bitmap[i + 3] = 0xff;
6213 }
6214 }
6215 else
6216 {
6217 /* [0] R, [1] G, [2] B, [3] A. */
6218 for (unsigned i = 0; i < cbData; i += 4)
6219 {
6220 bitmap[i] = pu8Data[i + 2];
6221 bitmap[i + 1] = pu8Data[i + 1];
6222 bitmap[i + 2] = pu8Data[i];
6223 bitmap[i + 3] = 0xff;
6224 }
6225 }
6226 bitmap.detachTo(ComSafeArrayOutArg(aData));
6227
6228 freeSavedDisplayScreenshot(pu8Data);
6229
6230 return S_OK;
6231}
6232
6233
6234STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6235{
6236 LogFlowThisFunc(("\n"));
6237
6238 CheckComArgNotNull(aWidth);
6239 CheckComArgNotNull(aHeight);
6240 CheckComArgOutSafeArrayPointerValid(aData);
6241
6242 if (aScreenId != 0)
6243 return E_NOTIMPL;
6244
6245 AutoCaller autoCaller(this);
6246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6247
6248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6249
6250 uint8_t *pu8Data = NULL;
6251 uint32_t cbData = 0;
6252 uint32_t u32Width = 0;
6253 uint32_t u32Height = 0;
6254
6255 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6256
6257 if (RT_FAILURE(vrc))
6258 return setError(VBOX_E_IPRT_ERROR,
6259 tr("Saved screenshot data is not available (%Rrc)"),
6260 vrc);
6261
6262 *aWidth = u32Width;
6263 *aHeight = u32Height;
6264
6265 HRESULT rc = S_OK;
6266 uint8_t *pu8PNG = NULL;
6267 uint32_t cbPNG = 0;
6268 uint32_t cxPNG = 0;
6269 uint32_t cyPNG = 0;
6270
6271 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6272
6273 if (RT_SUCCESS(vrc))
6274 {
6275 com::SafeArray<BYTE> screenData(cbPNG);
6276 screenData.initFrom(pu8PNG, cbPNG);
6277 if (pu8PNG)
6278 RTMemFree(pu8PNG);
6279 screenData.detachTo(ComSafeArrayOutArg(aData));
6280 }
6281 else
6282 {
6283 if (pu8PNG)
6284 RTMemFree(pu8PNG);
6285 return setError(VBOX_E_IPRT_ERROR,
6286 tr("Could not convert screenshot to PNG (%Rrc)"),
6287 vrc);
6288 }
6289
6290 freeSavedDisplayScreenshot(pu8Data);
6291
6292 return rc;
6293}
6294
6295STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6296{
6297 LogFlowThisFunc(("\n"));
6298
6299 CheckComArgNotNull(aSize);
6300 CheckComArgNotNull(aWidth);
6301 CheckComArgNotNull(aHeight);
6302
6303 if (aScreenId != 0)
6304 return E_NOTIMPL;
6305
6306 AutoCaller autoCaller(this);
6307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6308
6309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 uint8_t *pu8Data = NULL;
6312 uint32_t cbData = 0;
6313 uint32_t u32Width = 0;
6314 uint32_t u32Height = 0;
6315
6316 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6317
6318 if (RT_FAILURE(vrc))
6319 return setError(VBOX_E_IPRT_ERROR,
6320 tr("Saved screenshot data is not available (%Rrc)"),
6321 vrc);
6322
6323 *aSize = cbData;
6324 *aWidth = u32Width;
6325 *aHeight = u32Height;
6326
6327 freeSavedDisplayScreenshot(pu8Data);
6328
6329 return S_OK;
6330}
6331
6332STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6333{
6334 LogFlowThisFunc(("\n"));
6335
6336 CheckComArgNotNull(aWidth);
6337 CheckComArgNotNull(aHeight);
6338 CheckComArgOutSafeArrayPointerValid(aData);
6339
6340 if (aScreenId != 0)
6341 return E_NOTIMPL;
6342
6343 AutoCaller autoCaller(this);
6344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6345
6346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 uint8_t *pu8Data = NULL;
6349 uint32_t cbData = 0;
6350 uint32_t u32Width = 0;
6351 uint32_t u32Height = 0;
6352
6353 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6354
6355 if (RT_FAILURE(vrc))
6356 return setError(VBOX_E_IPRT_ERROR,
6357 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6358 vrc);
6359
6360 *aWidth = u32Width;
6361 *aHeight = u32Height;
6362
6363 com::SafeArray<BYTE> png(cbData);
6364 png.initFrom(pu8Data, cbData);
6365 png.detachTo(ComSafeArrayOutArg(aData));
6366
6367 freeSavedDisplayScreenshot(pu8Data);
6368
6369 return S_OK;
6370}
6371
6372STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6373{
6374 HRESULT rc = S_OK;
6375 LogFlowThisFunc(("\n"));
6376
6377 AutoCaller autoCaller(this);
6378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6379
6380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 if (!mHWData->mCPUHotPlugEnabled)
6383 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6384
6385 if (aCpu >= mHWData->mCPUCount)
6386 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6387
6388 if (mHWData->mCPUAttached[aCpu])
6389 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6390
6391 alock.release();
6392 rc = onCPUChange(aCpu, false);
6393 alock.acquire();
6394 if (FAILED(rc)) return rc;
6395
6396 setModified(IsModified_MachineData);
6397 mHWData.backup();
6398 mHWData->mCPUAttached[aCpu] = true;
6399
6400 /* Save settings if online */
6401 if (Global::IsOnline(mData->mMachineState))
6402 saveSettings(NULL);
6403
6404 return S_OK;
6405}
6406
6407STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6408{
6409 HRESULT rc = S_OK;
6410 LogFlowThisFunc(("\n"));
6411
6412 AutoCaller autoCaller(this);
6413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6414
6415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6416
6417 if (!mHWData->mCPUHotPlugEnabled)
6418 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6419
6420 if (aCpu >= SchemaDefs::MaxCPUCount)
6421 return setError(E_INVALIDARG,
6422 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6423 SchemaDefs::MaxCPUCount);
6424
6425 if (!mHWData->mCPUAttached[aCpu])
6426 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6427
6428 /* CPU 0 can't be detached */
6429 if (aCpu == 0)
6430 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6431
6432 alock.release();
6433 rc = onCPUChange(aCpu, true);
6434 alock.acquire();
6435 if (FAILED(rc)) return rc;
6436
6437 setModified(IsModified_MachineData);
6438 mHWData.backup();
6439 mHWData->mCPUAttached[aCpu] = false;
6440
6441 /* Save settings if online */
6442 if (Global::IsOnline(mData->mMachineState))
6443 saveSettings(NULL);
6444
6445 return S_OK;
6446}
6447
6448STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6449{
6450 LogFlowThisFunc(("\n"));
6451
6452 CheckComArgNotNull(aCpuAttached);
6453
6454 *aCpuAttached = false;
6455
6456 AutoCaller autoCaller(this);
6457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6458
6459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 /* If hotplug is enabled the CPU is always enabled. */
6462 if (!mHWData->mCPUHotPlugEnabled)
6463 {
6464 if (aCpu < mHWData->mCPUCount)
6465 *aCpuAttached = true;
6466 }
6467 else
6468 {
6469 if (aCpu < SchemaDefs::MaxCPUCount)
6470 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6471 }
6472
6473 return S_OK;
6474}
6475
6476STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6477{
6478 CheckComArgOutPointerValid(aName);
6479
6480 AutoCaller autoCaller(this);
6481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6482
6483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6484
6485 Utf8Str log = queryLogFilename(aIdx);
6486 if (!RTFileExists(log.c_str()))
6487 log.setNull();
6488 log.cloneTo(aName);
6489
6490 return S_OK;
6491}
6492
6493STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6494{
6495 LogFlowThisFunc(("\n"));
6496 CheckComArgOutSafeArrayPointerValid(aData);
6497 if (aSize < 0)
6498 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6499
6500 AutoCaller autoCaller(this);
6501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6502
6503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6504
6505 HRESULT rc = S_OK;
6506 Utf8Str log = queryLogFilename(aIdx);
6507
6508 /* do not unnecessarily hold the lock while doing something which does
6509 * not need the lock and potentially takes a long time. */
6510 alock.release();
6511
6512 /* Limit the chunk size to 32K for now, as that gives better performance
6513 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6514 * One byte expands to approx. 25 bytes of breathtaking XML. */
6515 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6516 com::SafeArray<BYTE> logData(cbData);
6517
6518 RTFILE LogFile;
6519 int vrc = RTFileOpen(&LogFile, log.c_str(),
6520 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6521 if (RT_SUCCESS(vrc))
6522 {
6523 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6524 if (RT_SUCCESS(vrc))
6525 logData.resize(cbData);
6526 else
6527 rc = setError(VBOX_E_IPRT_ERROR,
6528 tr("Could not read log file '%s' (%Rrc)"),
6529 log.c_str(), vrc);
6530 RTFileClose(LogFile);
6531 }
6532 else
6533 rc = setError(VBOX_E_IPRT_ERROR,
6534 tr("Could not open log file '%s' (%Rrc)"),
6535 log.c_str(), vrc);
6536
6537 if (FAILED(rc))
6538 logData.resize(0);
6539 logData.detachTo(ComSafeArrayOutArg(aData));
6540
6541 return rc;
6542}
6543
6544
6545/**
6546 * Currently this method doesn't attach device to the running VM,
6547 * just makes sure it's plugged on next VM start.
6548 */
6549STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6550{
6551 AutoCaller autoCaller(this);
6552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6553
6554 // lock scope
6555 {
6556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 HRESULT rc = checkStateDependency(MutableStateDep);
6559 if (FAILED(rc)) return rc;
6560
6561 ChipsetType_T aChipset = ChipsetType_PIIX3;
6562 COMGETTER(ChipsetType)(&aChipset);
6563
6564 if (aChipset != ChipsetType_ICH9)
6565 {
6566 return setError(E_INVALIDARG,
6567 tr("Host PCI attachment only supported with ICH9 chipset"));
6568 }
6569
6570 // check if device with this host PCI address already attached
6571 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6572 it != mHWData->mPCIDeviceAssignments.end();
6573 ++it)
6574 {
6575 LONG iHostAddress = -1;
6576 ComPtr<PCIDeviceAttachment> pAttach;
6577 pAttach = *it;
6578 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6579 if (iHostAddress == hostAddress)
6580 return setError(E_INVALIDARG,
6581 tr("Device with host PCI address already attached to this VM"));
6582 }
6583
6584 ComObjPtr<PCIDeviceAttachment> pda;
6585 char name[32];
6586
6587 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6588 Bstr bname(name);
6589 pda.createObject();
6590 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6591 setModified(IsModified_MachineData);
6592 mHWData.backup();
6593 mHWData->mPCIDeviceAssignments.push_back(pda);
6594 }
6595
6596 return S_OK;
6597}
6598
6599/**
6600 * Currently this method doesn't detach device from the running VM,
6601 * just makes sure it's not plugged on next VM start.
6602 */
6603STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6604{
6605 AutoCaller autoCaller(this);
6606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6607
6608 ComObjPtr<PCIDeviceAttachment> pAttach;
6609 bool fRemoved = false;
6610 HRESULT rc;
6611
6612 // lock scope
6613 {
6614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6615
6616 rc = checkStateDependency(MutableStateDep);
6617 if (FAILED(rc)) return rc;
6618
6619 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6620 it != mHWData->mPCIDeviceAssignments.end();
6621 ++it)
6622 {
6623 LONG iHostAddress = -1;
6624 pAttach = *it;
6625 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6626 if (iHostAddress != -1 && iHostAddress == hostAddress)
6627 {
6628 setModified(IsModified_MachineData);
6629 mHWData.backup();
6630 mHWData->mPCIDeviceAssignments.remove(pAttach);
6631 fRemoved = true;
6632 break;
6633 }
6634 }
6635 }
6636
6637
6638 /* Fire event outside of the lock */
6639 if (fRemoved)
6640 {
6641 Assert(!pAttach.isNull());
6642 ComPtr<IEventSource> es;
6643 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6644 Assert(SUCCEEDED(rc));
6645 Bstr mid;
6646 rc = this->COMGETTER(Id)(mid.asOutParam());
6647 Assert(SUCCEEDED(rc));
6648 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6649 }
6650
6651 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6652 tr("No host PCI device %08x attached"),
6653 hostAddress
6654 );
6655}
6656
6657STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6658{
6659 CheckComArgOutSafeArrayPointerValid(aAssignments);
6660
6661 AutoCaller autoCaller(this);
6662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6663
6664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6665
6666 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6667 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6668
6669 return S_OK;
6670}
6671
6672STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6673{
6674 CheckComArgOutPointerValid(aBandwidthControl);
6675
6676 AutoCaller autoCaller(this);
6677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6678
6679 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6680
6681 return S_OK;
6682}
6683
6684STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6685{
6686 CheckComArgOutPointerValid(pfEnabled);
6687 AutoCaller autoCaller(this);
6688 HRESULT hrc = autoCaller.rc();
6689 if (SUCCEEDED(hrc))
6690 {
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6693 }
6694 return hrc;
6695}
6696
6697STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6698{
6699 AutoCaller autoCaller(this);
6700 HRESULT hrc = autoCaller.rc();
6701 if (SUCCEEDED(hrc))
6702 {
6703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6704 hrc = checkStateDependency(MutableStateDep);
6705 if (SUCCEEDED(hrc))
6706 {
6707 hrc = mHWData.backupEx();
6708 if (SUCCEEDED(hrc))
6709 {
6710 setModified(IsModified_MachineData);
6711 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6712 }
6713 }
6714 }
6715 return hrc;
6716}
6717
6718STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6719{
6720 CheckComArgOutPointerValid(pbstrConfig);
6721 AutoCaller autoCaller(this);
6722 HRESULT hrc = autoCaller.rc();
6723 if (SUCCEEDED(hrc))
6724 {
6725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6726 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6727 }
6728 return hrc;
6729}
6730
6731STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6732{
6733 CheckComArgStr(bstrConfig);
6734 AutoCaller autoCaller(this);
6735 HRESULT hrc = autoCaller.rc();
6736 if (SUCCEEDED(hrc))
6737 {
6738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6739 hrc = checkStateDependency(MutableStateDep);
6740 if (SUCCEEDED(hrc))
6741 {
6742 hrc = mHWData.backupEx();
6743 if (SUCCEEDED(hrc))
6744 {
6745 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6746 if (SUCCEEDED(hrc))
6747 setModified(IsModified_MachineData);
6748 }
6749 }
6750 }
6751 return hrc;
6752
6753}
6754
6755STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6756{
6757 CheckComArgOutPointerValid(pfAllow);
6758 AutoCaller autoCaller(this);
6759 HRESULT hrc = autoCaller.rc();
6760 if (SUCCEEDED(hrc))
6761 {
6762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6763 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6764 }
6765 return hrc;
6766}
6767
6768STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6769{
6770 AutoCaller autoCaller(this);
6771 HRESULT hrc = autoCaller.rc();
6772 if (SUCCEEDED(hrc))
6773 {
6774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6775 hrc = checkStateDependency(MutableStateDep);
6776 if (SUCCEEDED(hrc))
6777 {
6778 hrc = mHWData.backupEx();
6779 if (SUCCEEDED(hrc))
6780 {
6781 setModified(IsModified_MachineData);
6782 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6783 }
6784 }
6785 }
6786 return hrc;
6787}
6788
6789STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6790{
6791 CheckComArgOutPointerValid(pfEnabled);
6792 AutoCaller autoCaller(this);
6793 HRESULT hrc = autoCaller.rc();
6794 if (SUCCEEDED(hrc))
6795 {
6796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6797 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6798 }
6799 return hrc;
6800}
6801
6802STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6803{
6804 AutoCaller autoCaller(this);
6805 HRESULT hrc = autoCaller.rc();
6806 if (SUCCEEDED(hrc))
6807 {
6808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6809 hrc = checkStateDependency(MutableStateDep);
6810 if ( SUCCEEDED(hrc)
6811 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6812 {
6813 AutostartDb *autostartDb = mParent->getAutostartDb();
6814 int vrc;
6815
6816 if (fEnabled)
6817 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6818 else
6819 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6820
6821 if (RT_SUCCESS(vrc))
6822 {
6823 hrc = mHWData.backupEx();
6824 if (SUCCEEDED(hrc))
6825 {
6826 setModified(IsModified_MachineData);
6827 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6828 }
6829 }
6830 else if (vrc == VERR_NOT_SUPPORTED)
6831 hrc = setError(VBOX_E_NOT_SUPPORTED,
6832 tr("The VM autostart feature is not supported on this platform"));
6833 else if (vrc == VERR_PATH_NOT_FOUND)
6834 hrc = setError(E_FAIL,
6835 tr("The path to the autostart database is not set"));
6836 else
6837 hrc = setError(E_UNEXPECTED,
6838 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6839 fEnabled ? "Adding" : "Removing",
6840 mUserData->s.strName.c_str(), vrc);
6841 }
6842 }
6843 return hrc;
6844}
6845
6846STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6847{
6848 CheckComArgOutPointerValid(puDelay);
6849 AutoCaller autoCaller(this);
6850 HRESULT hrc = autoCaller.rc();
6851 if (SUCCEEDED(hrc))
6852 {
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 *puDelay = mHWData->mAutostart.uAutostartDelay;
6855 }
6856 return hrc;
6857}
6858
6859STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6860{
6861 AutoCaller autoCaller(this);
6862 HRESULT hrc = autoCaller.rc();
6863 if (SUCCEEDED(hrc))
6864 {
6865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6866 hrc = checkStateDependency(MutableStateDep);
6867 if (SUCCEEDED(hrc))
6868 {
6869 hrc = mHWData.backupEx();
6870 if (SUCCEEDED(hrc))
6871 {
6872 setModified(IsModified_MachineData);
6873 mHWData->mAutostart.uAutostartDelay = uDelay;
6874 }
6875 }
6876 }
6877 return hrc;
6878}
6879
6880STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6881{
6882 CheckComArgOutPointerValid(penmAutostopType);
6883 AutoCaller autoCaller(this);
6884 HRESULT hrc = autoCaller.rc();
6885 if (SUCCEEDED(hrc))
6886 {
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6889 }
6890 return hrc;
6891}
6892
6893STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6894{
6895 AutoCaller autoCaller(this);
6896 HRESULT hrc = autoCaller.rc();
6897 if (SUCCEEDED(hrc))
6898 {
6899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6900 hrc = checkStateDependency(MutableStateDep);
6901 if ( SUCCEEDED(hrc)
6902 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6903 {
6904 AutostartDb *autostartDb = mParent->getAutostartDb();
6905 int vrc;
6906
6907 if (enmAutostopType != AutostopType_Disabled)
6908 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6909 else
6910 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6911
6912 if (RT_SUCCESS(vrc))
6913 {
6914 hrc = mHWData.backupEx();
6915 if (SUCCEEDED(hrc))
6916 {
6917 setModified(IsModified_MachineData);
6918 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6919 }
6920 }
6921 else if (vrc == VERR_NOT_SUPPORTED)
6922 hrc = setError(VBOX_E_NOT_SUPPORTED,
6923 tr("The VM autostop feature is not supported on this platform"));
6924 else if (vrc == VERR_PATH_NOT_FOUND)
6925 hrc = setError(E_FAIL,
6926 tr("The path to the autostart database is not set"));
6927 else
6928 hrc = setError(E_UNEXPECTED,
6929 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6930 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6931 mUserData->s.strName.c_str(), vrc);
6932 }
6933 }
6934 return hrc;
6935}
6936
6937
6938STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6939{
6940 LogFlowFuncEnter();
6941
6942 CheckComArgNotNull(pTarget);
6943 CheckComArgOutPointerValid(pProgress);
6944
6945 /* Convert the options. */
6946 RTCList<CloneOptions_T> optList;
6947 if (options != NULL)
6948 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6949
6950 if (optList.contains(CloneOptions_Link))
6951 {
6952 if (!isSnapshotMachine())
6953 return setError(E_INVALIDARG,
6954 tr("Linked clone can only be created from a snapshot"));
6955 if (mode != CloneMode_MachineState)
6956 return setError(E_INVALIDARG,
6957 tr("Linked clone can only be created for a single machine state"));
6958 }
6959 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6960
6961 AutoCaller autoCaller(this);
6962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6963
6964
6965 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6966
6967 HRESULT rc = pWorker->start(pProgress);
6968
6969 LogFlowFuncLeave();
6970
6971 return rc;
6972}
6973
6974// public methods for internal purposes
6975/////////////////////////////////////////////////////////////////////////////
6976
6977/**
6978 * Adds the given IsModified_* flag to the dirty flags of the machine.
6979 * This must be called either during loadSettings or under the machine write lock.
6980 * @param fl
6981 */
6982void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6983{
6984 mData->flModifications |= fl;
6985 if (fAllowStateModification && isStateModificationAllowed())
6986 mData->mCurrentStateModified = true;
6987}
6988
6989/**
6990 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6991 * care of the write locking.
6992 *
6993 * @param fModifications The flag to add.
6994 */
6995void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6996{
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998 setModified(fModification, fAllowStateModification);
6999}
7000
7001/**
7002 * Saves the registry entry of this machine to the given configuration node.
7003 *
7004 * @param aEntryNode Node to save the registry entry to.
7005 *
7006 * @note locks this object for reading.
7007 */
7008HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7009{
7010 AutoLimitedCaller autoCaller(this);
7011 AssertComRCReturnRC(autoCaller.rc());
7012
7013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7014
7015 data.uuid = mData->mUuid;
7016 data.strSettingsFile = mData->m_strConfigFile;
7017
7018 return S_OK;
7019}
7020
7021/**
7022 * Calculates the absolute path of the given path taking the directory of the
7023 * machine settings file as the current directory.
7024 *
7025 * @param aPath Path to calculate the absolute path for.
7026 * @param aResult Where to put the result (used only on success, can be the
7027 * same Utf8Str instance as passed in @a aPath).
7028 * @return IPRT result.
7029 *
7030 * @note Locks this object for reading.
7031 */
7032int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7033{
7034 AutoCaller autoCaller(this);
7035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7036
7037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7040
7041 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7042
7043 strSettingsDir.stripFilename();
7044 char folder[RTPATH_MAX];
7045 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7046 if (RT_SUCCESS(vrc))
7047 aResult = folder;
7048
7049 return vrc;
7050}
7051
7052/**
7053 * Copies strSource to strTarget, making it relative to the machine folder
7054 * if it is a subdirectory thereof, or simply copying it otherwise.
7055 *
7056 * @param strSource Path to evaluate and copy.
7057 * @param strTarget Buffer to receive target path.
7058 *
7059 * @note Locks this object for reading.
7060 */
7061void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7062 Utf8Str &strTarget)
7063{
7064 AutoCaller autoCaller(this);
7065 AssertComRCReturn(autoCaller.rc(), (void)0);
7066
7067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7068
7069 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7070 // use strTarget as a temporary buffer to hold the machine settings dir
7071 strTarget = mData->m_strConfigFileFull;
7072 strTarget.stripFilename();
7073 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7074 {
7075 // is relative: then append what's left
7076 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7077 // for empty paths (only possible for subdirs) use "." to avoid
7078 // triggering default settings for not present config attributes.
7079 if (strTarget.isEmpty())
7080 strTarget = ".";
7081 }
7082 else
7083 // is not relative: then overwrite
7084 strTarget = strSource;
7085}
7086
7087/**
7088 * Returns the full path to the machine's log folder in the
7089 * \a aLogFolder argument.
7090 */
7091void Machine::getLogFolder(Utf8Str &aLogFolder)
7092{
7093 AutoCaller autoCaller(this);
7094 AssertComRCReturnVoid(autoCaller.rc());
7095
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 char szTmp[RTPATH_MAX];
7099 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7100 if (RT_SUCCESS(vrc))
7101 {
7102 if (szTmp[0] && !mUserData.isNull())
7103 {
7104 char szTmp2[RTPATH_MAX];
7105 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7106 if (RT_SUCCESS(vrc))
7107 aLogFolder = BstrFmt("%s%c%s",
7108 szTmp2,
7109 RTPATH_DELIMITER,
7110 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7111 }
7112 else
7113 vrc = VERR_PATH_IS_RELATIVE;
7114 }
7115
7116 if (RT_FAILURE(vrc))
7117 {
7118 // fallback if VBOX_USER_LOGHOME is not set or invalid
7119 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7120 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7121 aLogFolder.append(RTPATH_DELIMITER);
7122 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7123 }
7124}
7125
7126/**
7127 * Returns the full path to the machine's log file for an given index.
7128 */
7129Utf8Str Machine::queryLogFilename(ULONG idx)
7130{
7131 Utf8Str logFolder;
7132 getLogFolder(logFolder);
7133 Assert(logFolder.length());
7134 Utf8Str log;
7135 if (idx == 0)
7136 log = Utf8StrFmt("%s%cVBox.log",
7137 logFolder.c_str(), RTPATH_DELIMITER);
7138 else
7139 log = Utf8StrFmt("%s%cVBox.log.%d",
7140 logFolder.c_str(), RTPATH_DELIMITER, idx);
7141 return log;
7142}
7143
7144/**
7145 * Composes a unique saved state filename based on the current system time. The filename is
7146 * granular to the second so this will work so long as no more than one snapshot is taken on
7147 * a machine per second.
7148 *
7149 * Before version 4.1, we used this formula for saved state files:
7150 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7151 * which no longer works because saved state files can now be shared between the saved state of the
7152 * "saved" machine and an online snapshot, and the following would cause problems:
7153 * 1) save machine
7154 * 2) create online snapshot from that machine state --> reusing saved state file
7155 * 3) save machine again --> filename would be reused, breaking the online snapshot
7156 *
7157 * So instead we now use a timestamp.
7158 *
7159 * @param str
7160 */
7161void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturnVoid(autoCaller.rc());
7165
7166 {
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7169 }
7170
7171 RTTIMESPEC ts;
7172 RTTimeNow(&ts);
7173 RTTIME time;
7174 RTTimeExplode(&time, &ts);
7175
7176 strStateFilePath += RTPATH_DELIMITER;
7177 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7178 time.i32Year, time.u8Month, time.u8MonthDay,
7179 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7180}
7181
7182/**
7183 * @note Locks this object for writing, calls the client process
7184 * (inside the lock).
7185 */
7186HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7187 const Utf8Str &strType,
7188 const Utf8Str &strEnvironment,
7189 ProgressProxy *aProgress)
7190{
7191 LogFlowThisFuncEnter();
7192
7193 AssertReturn(aControl, E_FAIL);
7194 AssertReturn(aProgress, E_FAIL);
7195
7196 AutoCaller autoCaller(this);
7197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7198
7199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 if (!mData->mRegistered)
7202 return setError(E_UNEXPECTED,
7203 tr("The machine '%s' is not registered"),
7204 mUserData->s.strName.c_str());
7205
7206 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7207
7208 if ( mData->mSession.mState == SessionState_Locked
7209 || mData->mSession.mState == SessionState_Spawning
7210 || mData->mSession.mState == SessionState_Unlocking)
7211 return setError(VBOX_E_INVALID_OBJECT_STATE,
7212 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7213 mUserData->s.strName.c_str());
7214
7215 /* may not be busy */
7216 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7217
7218 /* get the path to the executable */
7219 char szPath[RTPATH_MAX];
7220 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7221 size_t sz = strlen(szPath);
7222 szPath[sz++] = RTPATH_DELIMITER;
7223 szPath[sz] = 0;
7224 char *cmd = szPath + sz;
7225 sz = RTPATH_MAX - sz;
7226
7227 int vrc = VINF_SUCCESS;
7228 RTPROCESS pid = NIL_RTPROCESS;
7229
7230 RTENV env = RTENV_DEFAULT;
7231
7232 if (!strEnvironment.isEmpty())
7233 {
7234 char *newEnvStr = NULL;
7235
7236 do
7237 {
7238 /* clone the current environment */
7239 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7240 AssertRCBreakStmt(vrc2, vrc = vrc2);
7241
7242 newEnvStr = RTStrDup(strEnvironment.c_str());
7243 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7244
7245 /* put new variables to the environment
7246 * (ignore empty variable names here since RTEnv API
7247 * intentionally doesn't do that) */
7248 char *var = newEnvStr;
7249 for (char *p = newEnvStr; *p; ++p)
7250 {
7251 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7252 {
7253 *p = '\0';
7254 if (*var)
7255 {
7256 char *val = strchr(var, '=');
7257 if (val)
7258 {
7259 *val++ = '\0';
7260 vrc2 = RTEnvSetEx(env, var, val);
7261 }
7262 else
7263 vrc2 = RTEnvUnsetEx(env, var);
7264 if (RT_FAILURE(vrc2))
7265 break;
7266 }
7267 var = p + 1;
7268 }
7269 }
7270 if (RT_SUCCESS(vrc2) && *var)
7271 vrc2 = RTEnvPutEx(env, var);
7272
7273 AssertRCBreakStmt(vrc2, vrc = vrc2);
7274 }
7275 while (0);
7276
7277 if (newEnvStr != NULL)
7278 RTStrFree(newEnvStr);
7279 }
7280
7281 /* Qt is default */
7282#ifdef VBOX_WITH_QTGUI
7283 if (strType == "gui" || strType == "GUI/Qt")
7284 {
7285# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7286 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7287# else
7288 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7289# endif
7290 Assert(sz >= sizeof(VirtualBox_exe));
7291 strcpy(cmd, VirtualBox_exe);
7292
7293 Utf8Str idStr = mData->mUuid.toString();
7294 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7295 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7296 }
7297#else /* !VBOX_WITH_QTGUI */
7298 if (0)
7299 ;
7300#endif /* VBOX_WITH_QTGUI */
7301
7302 else
7303
7304#ifdef VBOX_WITH_VBOXSDL
7305 if (strType == "sdl" || strType == "GUI/SDL")
7306 {
7307 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7308 Assert(sz >= sizeof(VBoxSDL_exe));
7309 strcpy(cmd, VBoxSDL_exe);
7310
7311 Utf8Str idStr = mData->mUuid.toString();
7312 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7313 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7314 }
7315#else /* !VBOX_WITH_VBOXSDL */
7316 if (0)
7317 ;
7318#endif /* !VBOX_WITH_VBOXSDL */
7319
7320 else
7321
7322#ifdef VBOX_WITH_HEADLESS
7323 if ( strType == "headless"
7324 || strType == "capture"
7325 || strType == "vrdp" /* Deprecated. Same as headless. */
7326 )
7327 {
7328 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7329 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7330 * and a VM works even if the server has not been installed.
7331 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7332 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7333 * differently in 4.0 and 3.x.
7334 */
7335 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7336 Assert(sz >= sizeof(VBoxHeadless_exe));
7337 strcpy(cmd, VBoxHeadless_exe);
7338
7339 Utf8Str idStr = mData->mUuid.toString();
7340 /* Leave space for "--capture" arg. */
7341 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7342 "--startvm", idStr.c_str(),
7343 "--vrde", "config",
7344 0, /* For "--capture". */
7345 0 };
7346 if (strType == "capture")
7347 {
7348 unsigned pos = RT_ELEMENTS(args) - 2;
7349 args[pos] = "--capture";
7350 }
7351 vrc = RTProcCreate(szPath, args, env,
7352#ifdef RT_OS_WINDOWS
7353 RTPROC_FLAGS_NO_WINDOW
7354#else
7355 0
7356#endif
7357 , &pid);
7358 }
7359#else /* !VBOX_WITH_HEADLESS */
7360 if (0)
7361 ;
7362#endif /* !VBOX_WITH_HEADLESS */
7363 else
7364 {
7365 RTEnvDestroy(env);
7366 return setError(E_INVALIDARG,
7367 tr("Invalid session type: '%s'"),
7368 strType.c_str());
7369 }
7370
7371 RTEnvDestroy(env);
7372
7373 if (RT_FAILURE(vrc))
7374 return setError(VBOX_E_IPRT_ERROR,
7375 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7376 mUserData->s.strName.c_str(), vrc);
7377
7378 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7379
7380 /*
7381 * Note that we don't release the lock here before calling the client,
7382 * because it doesn't need to call us back if called with a NULL argument.
7383 * Releasing the lock here is dangerous because we didn't prepare the
7384 * launch data yet, but the client we've just started may happen to be
7385 * too fast and call openSession() that will fail (because of PID, etc.),
7386 * so that the Machine will never get out of the Spawning session state.
7387 */
7388
7389 /* inform the session that it will be a remote one */
7390 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7391 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7392 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7393
7394 if (FAILED(rc))
7395 {
7396 /* restore the session state */
7397 mData->mSession.mState = SessionState_Unlocked;
7398 /* The failure may occur w/o any error info (from RPC), so provide one */
7399 return setError(VBOX_E_VM_ERROR,
7400 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7401 }
7402
7403 /* attach launch data to the machine */
7404 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7405 mData->mSession.mRemoteControls.push_back(aControl);
7406 mData->mSession.mProgress = aProgress;
7407 mData->mSession.mPID = pid;
7408 mData->mSession.mState = SessionState_Spawning;
7409 mData->mSession.mType = strType;
7410
7411 LogFlowThisFuncLeave();
7412 return S_OK;
7413}
7414
7415/**
7416 * Returns @c true if the given machine has an open direct session and returns
7417 * the session machine instance and additional session data (on some platforms)
7418 * if so.
7419 *
7420 * Note that when the method returns @c false, the arguments remain unchanged.
7421 *
7422 * @param aMachine Session machine object.
7423 * @param aControl Direct session control object (optional).
7424 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7425 *
7426 * @note locks this object for reading.
7427 */
7428#if defined(RT_OS_WINDOWS)
7429bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7430 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7431 HANDLE *aIPCSem /*= NULL*/,
7432 bool aAllowClosing /*= false*/)
7433#elif defined(RT_OS_OS2)
7434bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7435 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7436 HMTX *aIPCSem /*= NULL*/,
7437 bool aAllowClosing /*= false*/)
7438#else
7439bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7440 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7441 bool aAllowClosing /*= false*/)
7442#endif
7443{
7444 AutoLimitedCaller autoCaller(this);
7445 AssertComRCReturn(autoCaller.rc(), false);
7446
7447 /* just return false for inaccessible machines */
7448 if (autoCaller.state() != Ready)
7449 return false;
7450
7451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 if ( mData->mSession.mState == SessionState_Locked
7454 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7455 )
7456 {
7457 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7458
7459 aMachine = mData->mSession.mMachine;
7460
7461 if (aControl != NULL)
7462 *aControl = mData->mSession.mDirectControl;
7463
7464#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7465 /* Additional session data */
7466 if (aIPCSem != NULL)
7467 *aIPCSem = aMachine->mIPCSem;
7468#endif
7469 return true;
7470 }
7471
7472 return false;
7473}
7474
7475/**
7476 * Returns @c true if the given machine has an spawning direct session and
7477 * returns and additional session data (on some platforms) if so.
7478 *
7479 * Note that when the method returns @c false, the arguments remain unchanged.
7480 *
7481 * @param aPID PID of the spawned direct session process.
7482 *
7483 * @note locks this object for reading.
7484 */
7485#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7486bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7487#else
7488bool Machine::isSessionSpawning()
7489#endif
7490{
7491 AutoLimitedCaller autoCaller(this);
7492 AssertComRCReturn(autoCaller.rc(), false);
7493
7494 /* just return false for inaccessible machines */
7495 if (autoCaller.state() != Ready)
7496 return false;
7497
7498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7499
7500 if (mData->mSession.mState == SessionState_Spawning)
7501 {
7502#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7503 /* Additional session data */
7504 if (aPID != NULL)
7505 {
7506 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7507 *aPID = mData->mSession.mPID;
7508 }
7509#endif
7510 return true;
7511 }
7512
7513 return false;
7514}
7515
7516/**
7517 * Called from the client watcher thread to check for unexpected client process
7518 * death during Session_Spawning state (e.g. before it successfully opened a
7519 * direct session).
7520 *
7521 * On Win32 and on OS/2, this method is called only when we've got the
7522 * direct client's process termination notification, so it always returns @c
7523 * true.
7524 *
7525 * On other platforms, this method returns @c true if the client process is
7526 * terminated and @c false if it's still alive.
7527 *
7528 * @note Locks this object for writing.
7529 */
7530bool Machine::checkForSpawnFailure()
7531{
7532 AutoCaller autoCaller(this);
7533 if (!autoCaller.isOk())
7534 {
7535 /* nothing to do */
7536 LogFlowThisFunc(("Already uninitialized!\n"));
7537 return true;
7538 }
7539
7540 /* VirtualBox::addProcessToReap() needs a write lock */
7541 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7542
7543 if (mData->mSession.mState != SessionState_Spawning)
7544 {
7545 /* nothing to do */
7546 LogFlowThisFunc(("Not spawning any more!\n"));
7547 return true;
7548 }
7549
7550 HRESULT rc = S_OK;
7551
7552#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7553
7554 /* the process was already unexpectedly terminated, we just need to set an
7555 * error and finalize session spawning */
7556 rc = setError(E_FAIL,
7557 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7558 getName().c_str());
7559#else
7560
7561 /* PID not yet initialized, skip check. */
7562 if (mData->mSession.mPID == NIL_RTPROCESS)
7563 return false;
7564
7565 RTPROCSTATUS status;
7566 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7567 &status);
7568
7569 if (vrc != VERR_PROCESS_RUNNING)
7570 {
7571 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7572 rc = setError(E_FAIL,
7573 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7574 getName().c_str(), status.iStatus);
7575 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7576 rc = setError(E_FAIL,
7577 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7578 getName().c_str(), status.iStatus);
7579 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7580 rc = setError(E_FAIL,
7581 tr("The virtual machine '%s' has terminated abnormally"),
7582 getName().c_str(), status.iStatus);
7583 else
7584 rc = setError(E_FAIL,
7585 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7586 getName().c_str(), rc);
7587 }
7588
7589#endif
7590
7591 if (FAILED(rc))
7592 {
7593 /* Close the remote session, remove the remote control from the list
7594 * and reset session state to Closed (@note keep the code in sync with
7595 * the relevant part in checkForSpawnFailure()). */
7596
7597 Assert(mData->mSession.mRemoteControls.size() == 1);
7598 if (mData->mSession.mRemoteControls.size() == 1)
7599 {
7600 ErrorInfoKeeper eik;
7601 mData->mSession.mRemoteControls.front()->Uninitialize();
7602 }
7603
7604 mData->mSession.mRemoteControls.clear();
7605 mData->mSession.mState = SessionState_Unlocked;
7606
7607 /* finalize the progress after setting the state */
7608 if (!mData->mSession.mProgress.isNull())
7609 {
7610 mData->mSession.mProgress->notifyComplete(rc);
7611 mData->mSession.mProgress.setNull();
7612 }
7613
7614 mParent->addProcessToReap(mData->mSession.mPID);
7615 mData->mSession.mPID = NIL_RTPROCESS;
7616
7617 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7618 return true;
7619 }
7620
7621 return false;
7622}
7623
7624/**
7625 * Checks whether the machine can be registered. If so, commits and saves
7626 * all settings.
7627 *
7628 * @note Must be called from mParent's write lock. Locks this object and
7629 * children for writing.
7630 */
7631HRESULT Machine::prepareRegister()
7632{
7633 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7634
7635 AutoLimitedCaller autoCaller(this);
7636 AssertComRCReturnRC(autoCaller.rc());
7637
7638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7639
7640 /* wait for state dependents to drop to zero */
7641 ensureNoStateDependencies();
7642
7643 if (!mData->mAccessible)
7644 return setError(VBOX_E_INVALID_OBJECT_STATE,
7645 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7646 mUserData->s.strName.c_str(),
7647 mData->mUuid.toString().c_str());
7648
7649 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7650
7651 if (mData->mRegistered)
7652 return setError(VBOX_E_INVALID_OBJECT_STATE,
7653 tr("The machine '%s' with UUID {%s} is already registered"),
7654 mUserData->s.strName.c_str(),
7655 mData->mUuid.toString().c_str());
7656
7657 HRESULT rc = S_OK;
7658
7659 // Ensure the settings are saved. If we are going to be registered and
7660 // no config file exists yet, create it by calling saveSettings() too.
7661 if ( (mData->flModifications)
7662 || (!mData->pMachineConfigFile->fileExists())
7663 )
7664 {
7665 rc = saveSettings(NULL);
7666 // no need to check whether VirtualBox.xml needs saving too since
7667 // we can't have a machine XML file rename pending
7668 if (FAILED(rc)) return rc;
7669 }
7670
7671 /* more config checking goes here */
7672
7673 if (SUCCEEDED(rc))
7674 {
7675 /* we may have had implicit modifications we want to fix on success */
7676 commit();
7677
7678 mData->mRegistered = true;
7679 }
7680 else
7681 {
7682 /* we may have had implicit modifications we want to cancel on failure*/
7683 rollback(false /* aNotify */);
7684 }
7685
7686 return rc;
7687}
7688
7689/**
7690 * Increases the number of objects dependent on the machine state or on the
7691 * registered state. Guarantees that these two states will not change at least
7692 * until #releaseStateDependency() is called.
7693 *
7694 * Depending on the @a aDepType value, additional state checks may be made.
7695 * These checks will set extended error info on failure. See
7696 * #checkStateDependency() for more info.
7697 *
7698 * If this method returns a failure, the dependency is not added and the caller
7699 * is not allowed to rely on any particular machine state or registration state
7700 * value and may return the failed result code to the upper level.
7701 *
7702 * @param aDepType Dependency type to add.
7703 * @param aState Current machine state (NULL if not interested).
7704 * @param aRegistered Current registered state (NULL if not interested).
7705 *
7706 * @note Locks this object for writing.
7707 */
7708HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7709 MachineState_T *aState /* = NULL */,
7710 BOOL *aRegistered /* = NULL */)
7711{
7712 AutoCaller autoCaller(this);
7713 AssertComRCReturnRC(autoCaller.rc());
7714
7715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7716
7717 HRESULT rc = checkStateDependency(aDepType);
7718 if (FAILED(rc)) return rc;
7719
7720 {
7721 if (mData->mMachineStateChangePending != 0)
7722 {
7723 /* ensureNoStateDependencies() is waiting for state dependencies to
7724 * drop to zero so don't add more. It may make sense to wait a bit
7725 * and retry before reporting an error (since the pending state
7726 * transition should be really quick) but let's just assert for
7727 * now to see if it ever happens on practice. */
7728
7729 AssertFailed();
7730
7731 return setError(E_ACCESSDENIED,
7732 tr("Machine state change is in progress. Please retry the operation later."));
7733 }
7734
7735 ++mData->mMachineStateDeps;
7736 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7737 }
7738
7739 if (aState)
7740 *aState = mData->mMachineState;
7741 if (aRegistered)
7742 *aRegistered = mData->mRegistered;
7743
7744 return S_OK;
7745}
7746
7747/**
7748 * Decreases the number of objects dependent on the machine state.
7749 * Must always complete the #addStateDependency() call after the state
7750 * dependency is no more necessary.
7751 */
7752void Machine::releaseStateDependency()
7753{
7754 AutoCaller autoCaller(this);
7755 AssertComRCReturnVoid(autoCaller.rc());
7756
7757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7758
7759 /* releaseStateDependency() w/o addStateDependency()? */
7760 AssertReturnVoid(mData->mMachineStateDeps != 0);
7761 -- mData->mMachineStateDeps;
7762
7763 if (mData->mMachineStateDeps == 0)
7764 {
7765 /* inform ensureNoStateDependencies() that there are no more deps */
7766 if (mData->mMachineStateChangePending != 0)
7767 {
7768 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7769 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7770 }
7771 }
7772}
7773
7774// protected methods
7775/////////////////////////////////////////////////////////////////////////////
7776
7777/**
7778 * Performs machine state checks based on the @a aDepType value. If a check
7779 * fails, this method will set extended error info, otherwise it will return
7780 * S_OK. It is supposed, that on failure, the caller will immediately return
7781 * the return value of this method to the upper level.
7782 *
7783 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7784 *
7785 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7786 * current state of this machine object allows to change settings of the
7787 * machine (i.e. the machine is not registered, or registered but not running
7788 * and not saved). It is useful to call this method from Machine setters
7789 * before performing any change.
7790 *
7791 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7792 * as for MutableStateDep except that if the machine is saved, S_OK is also
7793 * returned. This is useful in setters which allow changing machine
7794 * properties when it is in the saved state.
7795 *
7796 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7797 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7798 * Aborted).
7799 *
7800 * @param aDepType Dependency type to check.
7801 *
7802 * @note Non Machine based classes should use #addStateDependency() and
7803 * #releaseStateDependency() methods or the smart AutoStateDependency
7804 * template.
7805 *
7806 * @note This method must be called from under this object's read or write
7807 * lock.
7808 */
7809HRESULT Machine::checkStateDependency(StateDependency aDepType)
7810{
7811 switch (aDepType)
7812 {
7813 case AnyStateDep:
7814 {
7815 break;
7816 }
7817 case MutableStateDep:
7818 {
7819 if ( mData->mRegistered
7820 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7821 || ( mData->mMachineState != MachineState_Paused
7822 && mData->mMachineState != MachineState_Running
7823 && mData->mMachineState != MachineState_Aborted
7824 && mData->mMachineState != MachineState_Teleported
7825 && mData->mMachineState != MachineState_PoweredOff
7826 )
7827 )
7828 )
7829 return setError(VBOX_E_INVALID_VM_STATE,
7830 tr("The machine is not mutable (state is %s)"),
7831 Global::stringifyMachineState(mData->mMachineState));
7832 break;
7833 }
7834 case MutableOrSavedStateDep:
7835 {
7836 if ( mData->mRegistered
7837 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7838 || ( mData->mMachineState != MachineState_Paused
7839 && mData->mMachineState != MachineState_Running
7840 && mData->mMachineState != MachineState_Aborted
7841 && mData->mMachineState != MachineState_Teleported
7842 && mData->mMachineState != MachineState_Saved
7843 && mData->mMachineState != MachineState_PoweredOff
7844 )
7845 )
7846 )
7847 return setError(VBOX_E_INVALID_VM_STATE,
7848 tr("The machine is not mutable (state is %s)"),
7849 Global::stringifyMachineState(mData->mMachineState));
7850 break;
7851 }
7852 case OfflineStateDep:
7853 {
7854 if ( mData->mRegistered
7855 && ( !isSessionMachine()
7856 || ( mData->mMachineState != MachineState_PoweredOff
7857 && mData->mMachineState != MachineState_Saved
7858 && mData->mMachineState != MachineState_Aborted
7859 && mData->mMachineState != MachineState_Teleported
7860 )
7861 )
7862 )
7863 return setError(VBOX_E_INVALID_VM_STATE,
7864 tr("The machine is not offline (state is %s)"),
7865 Global::stringifyMachineState(mData->mMachineState));
7866 break;
7867 }
7868 }
7869
7870 return S_OK;
7871}
7872
7873/**
7874 * Helper to initialize all associated child objects and allocate data
7875 * structures.
7876 *
7877 * This method must be called as a part of the object's initialization procedure
7878 * (usually done in the #init() method).
7879 *
7880 * @note Must be called only from #init() or from #registeredInit().
7881 */
7882HRESULT Machine::initDataAndChildObjects()
7883{
7884 AutoCaller autoCaller(this);
7885 AssertComRCReturnRC(autoCaller.rc());
7886 AssertComRCReturn(autoCaller.state() == InInit ||
7887 autoCaller.state() == Limited, E_FAIL);
7888
7889 AssertReturn(!mData->mAccessible, E_FAIL);
7890
7891 /* allocate data structures */
7892 mSSData.allocate();
7893 mUserData.allocate();
7894 mHWData.allocate();
7895 mMediaData.allocate();
7896 mStorageControllers.allocate();
7897
7898 /* initialize mOSTypeId */
7899 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7900
7901 /* create associated BIOS settings object */
7902 unconst(mBIOSSettings).createObject();
7903 mBIOSSettings->init(this);
7904
7905 /* create an associated VRDE object (default is disabled) */
7906 unconst(mVRDEServer).createObject();
7907 mVRDEServer->init(this);
7908
7909 /* create associated serial port objects */
7910 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7911 {
7912 unconst(mSerialPorts[slot]).createObject();
7913 mSerialPorts[slot]->init(this, slot);
7914 }
7915
7916 /* create associated parallel port objects */
7917 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7918 {
7919 unconst(mParallelPorts[slot]).createObject();
7920 mParallelPorts[slot]->init(this, slot);
7921 }
7922
7923 /* create the audio adapter object (always present, default is disabled) */
7924 unconst(mAudioAdapter).createObject();
7925 mAudioAdapter->init(this);
7926
7927 /* create the USB controller object (always present, default is disabled) */
7928 unconst(mUSBController).createObject();
7929 mUSBController->init(this);
7930
7931 /* create associated network adapter objects */
7932 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7933 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7934 {
7935 unconst(mNetworkAdapters[slot]).createObject();
7936 mNetworkAdapters[slot]->init(this, slot);
7937 }
7938
7939 /* create the bandwidth control */
7940 unconst(mBandwidthControl).createObject();
7941 mBandwidthControl->init(this);
7942
7943 return S_OK;
7944}
7945
7946/**
7947 * Helper to uninitialize all associated child objects and to free all data
7948 * structures.
7949 *
7950 * This method must be called as a part of the object's uninitialization
7951 * procedure (usually done in the #uninit() method).
7952 *
7953 * @note Must be called only from #uninit() or from #registeredInit().
7954 */
7955void Machine::uninitDataAndChildObjects()
7956{
7957 AutoCaller autoCaller(this);
7958 AssertComRCReturnVoid(autoCaller.rc());
7959 AssertComRCReturnVoid( autoCaller.state() == InUninit
7960 || autoCaller.state() == Limited);
7961
7962 /* tell all our other child objects we've been uninitialized */
7963 if (mBandwidthControl)
7964 {
7965 mBandwidthControl->uninit();
7966 unconst(mBandwidthControl).setNull();
7967 }
7968
7969 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7970 {
7971 if (mNetworkAdapters[slot])
7972 {
7973 mNetworkAdapters[slot]->uninit();
7974 unconst(mNetworkAdapters[slot]).setNull();
7975 }
7976 }
7977
7978 if (mUSBController)
7979 {
7980 mUSBController->uninit();
7981 unconst(mUSBController).setNull();
7982 }
7983
7984 if (mAudioAdapter)
7985 {
7986 mAudioAdapter->uninit();
7987 unconst(mAudioAdapter).setNull();
7988 }
7989
7990 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7991 {
7992 if (mParallelPorts[slot])
7993 {
7994 mParallelPorts[slot]->uninit();
7995 unconst(mParallelPorts[slot]).setNull();
7996 }
7997 }
7998
7999 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8000 {
8001 if (mSerialPorts[slot])
8002 {
8003 mSerialPorts[slot]->uninit();
8004 unconst(mSerialPorts[slot]).setNull();
8005 }
8006 }
8007
8008 if (mVRDEServer)
8009 {
8010 mVRDEServer->uninit();
8011 unconst(mVRDEServer).setNull();
8012 }
8013
8014 if (mBIOSSettings)
8015 {
8016 mBIOSSettings->uninit();
8017 unconst(mBIOSSettings).setNull();
8018 }
8019
8020 /* Deassociate media (only when a real Machine or a SnapshotMachine
8021 * instance is uninitialized; SessionMachine instances refer to real
8022 * Machine media). This is necessary for a clean re-initialization of
8023 * the VM after successfully re-checking the accessibility state. Note
8024 * that in case of normal Machine or SnapshotMachine uninitialization (as
8025 * a result of unregistering or deleting the snapshot), outdated media
8026 * attachments will already be uninitialized and deleted, so this
8027 * code will not affect them. */
8028 if ( !!mMediaData
8029 && (!isSessionMachine())
8030 )
8031 {
8032 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8033 it != mMediaData->mAttachments.end();
8034 ++it)
8035 {
8036 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8037 if (pMedium.isNull())
8038 continue;
8039 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8040 AssertComRC(rc);
8041 }
8042 }
8043
8044 if (!isSessionMachine() && !isSnapshotMachine())
8045 {
8046 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8047 if (mData->mFirstSnapshot)
8048 {
8049 // snapshots tree is protected by machine write lock; strictly
8050 // this isn't necessary here since we're deleting the entire
8051 // machine, but otherwise we assert in Snapshot::uninit()
8052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8053 mData->mFirstSnapshot->uninit();
8054 mData->mFirstSnapshot.setNull();
8055 }
8056
8057 mData->mCurrentSnapshot.setNull();
8058 }
8059
8060 /* free data structures (the essential mData structure is not freed here
8061 * since it may be still in use) */
8062 mMediaData.free();
8063 mStorageControllers.free();
8064 mHWData.free();
8065 mUserData.free();
8066 mSSData.free();
8067}
8068
8069/**
8070 * Returns a pointer to the Machine object for this machine that acts like a
8071 * parent for complex machine data objects such as shared folders, etc.
8072 *
8073 * For primary Machine objects and for SnapshotMachine objects, returns this
8074 * object's pointer itself. For SessionMachine objects, returns the peer
8075 * (primary) machine pointer.
8076 */
8077Machine* Machine::getMachine()
8078{
8079 if (isSessionMachine())
8080 return (Machine*)mPeer;
8081 return this;
8082}
8083
8084/**
8085 * Makes sure that there are no machine state dependents. If necessary, waits
8086 * for the number of dependents to drop to zero.
8087 *
8088 * Make sure this method is called from under this object's write lock to
8089 * guarantee that no new dependents may be added when this method returns
8090 * control to the caller.
8091 *
8092 * @note Locks this object for writing. The lock will be released while waiting
8093 * (if necessary).
8094 *
8095 * @warning To be used only in methods that change the machine state!
8096 */
8097void Machine::ensureNoStateDependencies()
8098{
8099 AssertReturnVoid(isWriteLockOnCurrentThread());
8100
8101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8102
8103 /* Wait for all state dependents if necessary */
8104 if (mData->mMachineStateDeps != 0)
8105 {
8106 /* lazy semaphore creation */
8107 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8108 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8109
8110 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8111 mData->mMachineStateDeps));
8112
8113 ++mData->mMachineStateChangePending;
8114
8115 /* reset the semaphore before waiting, the last dependent will signal
8116 * it */
8117 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8118
8119 alock.release();
8120
8121 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8122
8123 alock.acquire();
8124
8125 -- mData->mMachineStateChangePending;
8126 }
8127}
8128
8129/**
8130 * Changes the machine state and informs callbacks.
8131 *
8132 * This method is not intended to fail so it either returns S_OK or asserts (and
8133 * returns a failure).
8134 *
8135 * @note Locks this object for writing.
8136 */
8137HRESULT Machine::setMachineState(MachineState_T aMachineState)
8138{
8139 LogFlowThisFuncEnter();
8140 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8141
8142 AutoCaller autoCaller(this);
8143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8144
8145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8146
8147 /* wait for state dependents to drop to zero */
8148 ensureNoStateDependencies();
8149
8150 if (mData->mMachineState != aMachineState)
8151 {
8152 mData->mMachineState = aMachineState;
8153
8154 RTTimeNow(&mData->mLastStateChange);
8155
8156 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8157 }
8158
8159 LogFlowThisFuncLeave();
8160 return S_OK;
8161}
8162
8163/**
8164 * Searches for a shared folder with the given logical name
8165 * in the collection of shared folders.
8166 *
8167 * @param aName logical name of the shared folder
8168 * @param aSharedFolder where to return the found object
8169 * @param aSetError whether to set the error info if the folder is
8170 * not found
8171 * @return
8172 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8173 *
8174 * @note
8175 * must be called from under the object's lock!
8176 */
8177HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8178 ComObjPtr<SharedFolder> &aSharedFolder,
8179 bool aSetError /* = false */)
8180{
8181 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8182 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8183 it != mHWData->mSharedFolders.end();
8184 ++it)
8185 {
8186 SharedFolder *pSF = *it;
8187 AutoCaller autoCaller(pSF);
8188 if (pSF->getName() == aName)
8189 {
8190 aSharedFolder = pSF;
8191 rc = S_OK;
8192 break;
8193 }
8194 }
8195
8196 if (aSetError && FAILED(rc))
8197 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8198
8199 return rc;
8200}
8201
8202/**
8203 * Initializes all machine instance data from the given settings structures
8204 * from XML. The exception is the machine UUID which needs special handling
8205 * depending on the caller's use case, so the caller needs to set that herself.
8206 *
8207 * This gets called in several contexts during machine initialization:
8208 *
8209 * -- When machine XML exists on disk already and needs to be loaded into memory,
8210 * for example, from registeredInit() to load all registered machines on
8211 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8212 * attached to the machine should be part of some media registry already.
8213 *
8214 * -- During OVF import, when a machine config has been constructed from an
8215 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8216 * ensure that the media listed as attachments in the config (which have
8217 * been imported from the OVF) receive the correct registry ID.
8218 *
8219 * -- During VM cloning.
8220 *
8221 * @param config Machine settings from XML.
8222 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8223 * @return
8224 */
8225HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8226 const Guid *puuidRegistry)
8227{
8228 // copy name, description, OS type, teleporter, UTC etc.
8229 mUserData->s = config.machineUserData;
8230
8231 // look up the object by Id to check it is valid
8232 ComPtr<IGuestOSType> guestOSType;
8233 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8234 guestOSType.asOutParam());
8235 if (FAILED(rc)) return rc;
8236
8237 // stateFile (optional)
8238 if (config.strStateFile.isEmpty())
8239 mSSData->strStateFilePath.setNull();
8240 else
8241 {
8242 Utf8Str stateFilePathFull(config.strStateFile);
8243 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8244 if (RT_FAILURE(vrc))
8245 return setError(E_FAIL,
8246 tr("Invalid saved state file path '%s' (%Rrc)"),
8247 config.strStateFile.c_str(),
8248 vrc);
8249 mSSData->strStateFilePath = stateFilePathFull;
8250 }
8251
8252 // snapshot folder needs special processing so set it again
8253 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8254 if (FAILED(rc)) return rc;
8255
8256 /* Copy the extra data items (Not in any case config is already the same as
8257 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8258 * make sure the extra data map is copied). */
8259 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8260
8261 /* currentStateModified (optional, default is true) */
8262 mData->mCurrentStateModified = config.fCurrentStateModified;
8263
8264 mData->mLastStateChange = config.timeLastStateChange;
8265
8266 /*
8267 * note: all mUserData members must be assigned prior this point because
8268 * we need to commit changes in order to let mUserData be shared by all
8269 * snapshot machine instances.
8270 */
8271 mUserData.commitCopy();
8272
8273 // machine registry, if present (must be loaded before snapshots)
8274 if (config.canHaveOwnMediaRegistry())
8275 {
8276 // determine machine folder
8277 Utf8Str strMachineFolder = getSettingsFileFull();
8278 strMachineFolder.stripFilename();
8279 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8280 config.mediaRegistry,
8281 strMachineFolder);
8282 if (FAILED(rc)) return rc;
8283 }
8284
8285 /* Snapshot node (optional) */
8286 size_t cRootSnapshots;
8287 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8288 {
8289 // there must be only one root snapshot
8290 Assert(cRootSnapshots == 1);
8291
8292 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8293
8294 rc = loadSnapshot(snap,
8295 config.uuidCurrentSnapshot,
8296 NULL); // no parent == first snapshot
8297 if (FAILED(rc)) return rc;
8298 }
8299
8300 // hardware data
8301 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8302 if (FAILED(rc)) return rc;
8303
8304 // load storage controllers
8305 rc = loadStorageControllers(config.storageMachine,
8306 puuidRegistry,
8307 NULL /* puuidSnapshot */);
8308 if (FAILED(rc)) return rc;
8309
8310 /*
8311 * NOTE: the assignment below must be the last thing to do,
8312 * otherwise it will be not possible to change the settings
8313 * somewhere in the code above because all setters will be
8314 * blocked by checkStateDependency(MutableStateDep).
8315 */
8316
8317 /* set the machine state to Aborted or Saved when appropriate */
8318 if (config.fAborted)
8319 {
8320 mSSData->strStateFilePath.setNull();
8321
8322 /* no need to use setMachineState() during init() */
8323 mData->mMachineState = MachineState_Aborted;
8324 }
8325 else if (!mSSData->strStateFilePath.isEmpty())
8326 {
8327 /* no need to use setMachineState() during init() */
8328 mData->mMachineState = MachineState_Saved;
8329 }
8330
8331 // after loading settings, we are no longer different from the XML on disk
8332 mData->flModifications = 0;
8333
8334 return S_OK;
8335}
8336
8337/**
8338 * Recursively loads all snapshots starting from the given.
8339 *
8340 * @param aNode <Snapshot> node.
8341 * @param aCurSnapshotId Current snapshot ID from the settings file.
8342 * @param aParentSnapshot Parent snapshot.
8343 */
8344HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8345 const Guid &aCurSnapshotId,
8346 Snapshot *aParentSnapshot)
8347{
8348 AssertReturn(!isSnapshotMachine(), E_FAIL);
8349 AssertReturn(!isSessionMachine(), E_FAIL);
8350
8351 HRESULT rc = S_OK;
8352
8353 Utf8Str strStateFile;
8354 if (!data.strStateFile.isEmpty())
8355 {
8356 /* optional */
8357 strStateFile = data.strStateFile;
8358 int vrc = calculateFullPath(strStateFile, strStateFile);
8359 if (RT_FAILURE(vrc))
8360 return setError(E_FAIL,
8361 tr("Invalid saved state file path '%s' (%Rrc)"),
8362 strStateFile.c_str(),
8363 vrc);
8364 }
8365
8366 /* create a snapshot machine object */
8367 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8368 pSnapshotMachine.createObject();
8369 rc = pSnapshotMachine->initFromSettings(this,
8370 data.hardware,
8371 &data.debugging,
8372 &data.autostart,
8373 data.storage,
8374 data.uuid.ref(),
8375 strStateFile);
8376 if (FAILED(rc)) return rc;
8377
8378 /* create a snapshot object */
8379 ComObjPtr<Snapshot> pSnapshot;
8380 pSnapshot.createObject();
8381 /* initialize the snapshot */
8382 rc = pSnapshot->init(mParent, // VirtualBox object
8383 data.uuid,
8384 data.strName,
8385 data.strDescription,
8386 data.timestamp,
8387 pSnapshotMachine,
8388 aParentSnapshot);
8389 if (FAILED(rc)) return rc;
8390
8391 /* memorize the first snapshot if necessary */
8392 if (!mData->mFirstSnapshot)
8393 mData->mFirstSnapshot = pSnapshot;
8394
8395 /* memorize the current snapshot when appropriate */
8396 if ( !mData->mCurrentSnapshot
8397 && pSnapshot->getId() == aCurSnapshotId
8398 )
8399 mData->mCurrentSnapshot = pSnapshot;
8400
8401 // now create the children
8402 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8403 it != data.llChildSnapshots.end();
8404 ++it)
8405 {
8406 const settings::Snapshot &childData = *it;
8407 // recurse
8408 rc = loadSnapshot(childData,
8409 aCurSnapshotId,
8410 pSnapshot); // parent = the one we created above
8411 if (FAILED(rc)) return rc;
8412 }
8413
8414 return rc;
8415}
8416
8417/**
8418 * Loads settings into mHWData.
8419 *
8420 * @param data Reference to the hardware settings.
8421 * @param pDbg Pointer to the debugging settings.
8422 * @param pAutostart Pointer to the autostart settings.
8423 */
8424HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8425 const settings::Autostart *pAutostart)
8426{
8427 AssertReturn(!isSessionMachine(), E_FAIL);
8428
8429 HRESULT rc = S_OK;
8430
8431 try
8432 {
8433 /* The hardware version attribute (optional). */
8434 mHWData->mHWVersion = data.strVersion;
8435 mHWData->mHardwareUUID = data.uuid;
8436
8437 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8438 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8439 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8440 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8441 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8442 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8443 mHWData->mPAEEnabled = data.fPAE;
8444 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8445
8446 mHWData->mCPUCount = data.cCPUs;
8447 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8448 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8449
8450 // cpu
8451 if (mHWData->mCPUHotPlugEnabled)
8452 {
8453 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8454 it != data.llCpus.end();
8455 ++it)
8456 {
8457 const settings::Cpu &cpu = *it;
8458
8459 mHWData->mCPUAttached[cpu.ulId] = true;
8460 }
8461 }
8462
8463 // cpuid leafs
8464 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8465 it != data.llCpuIdLeafs.end();
8466 ++it)
8467 {
8468 const settings::CpuIdLeaf &leaf = *it;
8469
8470 switch (leaf.ulId)
8471 {
8472 case 0x0:
8473 case 0x1:
8474 case 0x2:
8475 case 0x3:
8476 case 0x4:
8477 case 0x5:
8478 case 0x6:
8479 case 0x7:
8480 case 0x8:
8481 case 0x9:
8482 case 0xA:
8483 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8484 break;
8485
8486 case 0x80000000:
8487 case 0x80000001:
8488 case 0x80000002:
8489 case 0x80000003:
8490 case 0x80000004:
8491 case 0x80000005:
8492 case 0x80000006:
8493 case 0x80000007:
8494 case 0x80000008:
8495 case 0x80000009:
8496 case 0x8000000A:
8497 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8498 break;
8499
8500 default:
8501 /* just ignore */
8502 break;
8503 }
8504 }
8505
8506 mHWData->mMemorySize = data.ulMemorySizeMB;
8507 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8508
8509 // boot order
8510 for (size_t i = 0;
8511 i < RT_ELEMENTS(mHWData->mBootOrder);
8512 i++)
8513 {
8514 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8515 if (it == data.mapBootOrder.end())
8516 mHWData->mBootOrder[i] = DeviceType_Null;
8517 else
8518 mHWData->mBootOrder[i] = it->second;
8519 }
8520
8521 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8522 mHWData->mMonitorCount = data.cMonitors;
8523 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8524 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8525 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8526 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8527 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8528 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8529 mHWData->mFirmwareType = data.firmwareType;
8530 mHWData->mPointingHIDType = data.pointingHIDType;
8531 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8532 mHWData->mChipsetType = data.chipsetType;
8533 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8534 mHWData->mHPETEnabled = data.fHPETEnabled;
8535
8536 /* VRDEServer */
8537 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8538 if (FAILED(rc)) return rc;
8539
8540 /* BIOS */
8541 rc = mBIOSSettings->loadSettings(data.biosSettings);
8542 if (FAILED(rc)) return rc;
8543
8544 // Bandwidth control (must come before network adapters)
8545 rc = mBandwidthControl->loadSettings(data.ioSettings);
8546 if (FAILED(rc)) return rc;
8547
8548 /* USB Controller */
8549 rc = mUSBController->loadSettings(data.usbController);
8550 if (FAILED(rc)) return rc;
8551
8552 // network adapters
8553 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8554 uint32_t oldCount = mNetworkAdapters.size();
8555 if (newCount > oldCount)
8556 {
8557 mNetworkAdapters.resize(newCount);
8558 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8559 {
8560 unconst(mNetworkAdapters[slot]).createObject();
8561 mNetworkAdapters[slot]->init(this, slot);
8562 }
8563 }
8564 else if (newCount < oldCount)
8565 mNetworkAdapters.resize(newCount);
8566 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8567 it != data.llNetworkAdapters.end();
8568 ++it)
8569 {
8570 const settings::NetworkAdapter &nic = *it;
8571
8572 /* slot unicity is guaranteed by XML Schema */
8573 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8574 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8575 if (FAILED(rc)) return rc;
8576 }
8577
8578 // serial ports
8579 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8580 it != data.llSerialPorts.end();
8581 ++it)
8582 {
8583 const settings::SerialPort &s = *it;
8584
8585 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8586 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8587 if (FAILED(rc)) return rc;
8588 }
8589
8590 // parallel ports (optional)
8591 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8592 it != data.llParallelPorts.end();
8593 ++it)
8594 {
8595 const settings::ParallelPort &p = *it;
8596
8597 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8598 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8599 if (FAILED(rc)) return rc;
8600 }
8601
8602 /* AudioAdapter */
8603 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8604 if (FAILED(rc)) return rc;
8605
8606 /* Shared folders */
8607 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8608 it != data.llSharedFolders.end();
8609 ++it)
8610 {
8611 const settings::SharedFolder &sf = *it;
8612
8613 ComObjPtr<SharedFolder> sharedFolder;
8614 /* Check for double entries. Not allowed! */
8615 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8616 if (SUCCEEDED(rc))
8617 return setError(VBOX_E_OBJECT_IN_USE,
8618 tr("Shared folder named '%s' already exists"),
8619 sf.strName.c_str());
8620
8621 /* Create the new shared folder. Don't break on error. This will be
8622 * reported when the machine starts. */
8623 sharedFolder.createObject();
8624 rc = sharedFolder->init(getMachine(),
8625 sf.strName,
8626 sf.strHostPath,
8627 RT_BOOL(sf.fWritable),
8628 RT_BOOL(sf.fAutoMount),
8629 false /* fFailOnError */);
8630 if (FAILED(rc)) return rc;
8631 mHWData->mSharedFolders.push_back(sharedFolder);
8632 }
8633
8634 // Clipboard
8635 mHWData->mClipboardMode = data.clipboardMode;
8636
8637 // drag'n'drop
8638 mHWData->mDragAndDropMode = data.dragAndDropMode;
8639
8640 // guest settings
8641 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8642
8643 // IO settings
8644 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8645 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8646
8647 // Host PCI devices
8648 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8649 it != data.pciAttachments.end();
8650 ++it)
8651 {
8652 const settings::HostPCIDeviceAttachment &hpda = *it;
8653 ComObjPtr<PCIDeviceAttachment> pda;
8654
8655 pda.createObject();
8656 pda->loadSettings(this, hpda);
8657 mHWData->mPCIDeviceAssignments.push_back(pda);
8658 }
8659
8660 /*
8661 * (The following isn't really real hardware, but it lives in HWData
8662 * for reasons of convenience.)
8663 */
8664
8665#ifdef VBOX_WITH_GUEST_PROPS
8666 /* Guest properties (optional) */
8667 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8668 it != data.llGuestProperties.end();
8669 ++it)
8670 {
8671 const settings::GuestProperty &prop = *it;
8672 uint32_t fFlags = guestProp::NILFLAG;
8673 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8674 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8675 mHWData->mGuestProperties.push_back(property);
8676 }
8677
8678 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8679#endif /* VBOX_WITH_GUEST_PROPS defined */
8680
8681 rc = loadDebugging(pDbg);
8682 if (FAILED(rc))
8683 return rc;
8684
8685 mHWData->mAutostart = *pAutostart;
8686 }
8687 catch(std::bad_alloc &)
8688 {
8689 return E_OUTOFMEMORY;
8690 }
8691
8692 AssertComRC(rc);
8693 return rc;
8694}
8695
8696/**
8697 * Called from Machine::loadHardware() to load the debugging settings of the
8698 * machine.
8699 *
8700 * @param pDbg Pointer to the settings.
8701 */
8702HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8703{
8704 mHWData->mDebugging = *pDbg;
8705 /* no more processing currently required, this will probably change. */
8706 return S_OK;
8707}
8708
8709/**
8710 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8711 *
8712 * @param data
8713 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8714 * @param puuidSnapshot
8715 * @return
8716 */
8717HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8718 const Guid *puuidRegistry,
8719 const Guid *puuidSnapshot)
8720{
8721 AssertReturn(!isSessionMachine(), E_FAIL);
8722
8723 HRESULT rc = S_OK;
8724
8725 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8726 it != data.llStorageControllers.end();
8727 ++it)
8728 {
8729 const settings::StorageController &ctlData = *it;
8730
8731 ComObjPtr<StorageController> pCtl;
8732 /* Try to find one with the name first. */
8733 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8734 if (SUCCEEDED(rc))
8735 return setError(VBOX_E_OBJECT_IN_USE,
8736 tr("Storage controller named '%s' already exists"),
8737 ctlData.strName.c_str());
8738
8739 pCtl.createObject();
8740 rc = pCtl->init(this,
8741 ctlData.strName,
8742 ctlData.storageBus,
8743 ctlData.ulInstance,
8744 ctlData.fBootable);
8745 if (FAILED(rc)) return rc;
8746
8747 mStorageControllers->push_back(pCtl);
8748
8749 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8750 if (FAILED(rc)) return rc;
8751
8752 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8753 if (FAILED(rc)) return rc;
8754
8755 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8756 if (FAILED(rc)) return rc;
8757
8758 /* Set IDE emulation settings (only for AHCI controller). */
8759 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8760 {
8761 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8762 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8763 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8764 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8765 )
8766 return rc;
8767 }
8768
8769 /* Load the attached devices now. */
8770 rc = loadStorageDevices(pCtl,
8771 ctlData,
8772 puuidRegistry,
8773 puuidSnapshot);
8774 if (FAILED(rc)) return rc;
8775 }
8776
8777 return S_OK;
8778}
8779
8780/**
8781 * Called from loadStorageControllers for a controller's devices.
8782 *
8783 * @param aStorageController
8784 * @param data
8785 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8786 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8787 * @return
8788 */
8789HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8790 const settings::StorageController &data,
8791 const Guid *puuidRegistry,
8792 const Guid *puuidSnapshot)
8793{
8794 HRESULT rc = S_OK;
8795
8796 /* paranoia: detect duplicate attachments */
8797 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8798 it != data.llAttachedDevices.end();
8799 ++it)
8800 {
8801 const settings::AttachedDevice &ad = *it;
8802
8803 for (settings::AttachedDevicesList::const_iterator it2 = it;
8804 it2 != data.llAttachedDevices.end();
8805 ++it2)
8806 {
8807 if (it == it2)
8808 continue;
8809
8810 const settings::AttachedDevice &ad2 = *it2;
8811
8812 if ( ad.lPort == ad2.lPort
8813 && ad.lDevice == ad2.lDevice)
8814 {
8815 return setError(E_FAIL,
8816 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8817 aStorageController->getName().c_str(),
8818 ad.lPort,
8819 ad.lDevice,
8820 mUserData->s.strName.c_str());
8821 }
8822 }
8823 }
8824
8825 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8826 it != data.llAttachedDevices.end();
8827 ++it)
8828 {
8829 const settings::AttachedDevice &dev = *it;
8830 ComObjPtr<Medium> medium;
8831
8832 switch (dev.deviceType)
8833 {
8834 case DeviceType_Floppy:
8835 case DeviceType_DVD:
8836 if (dev.strHostDriveSrc.isNotEmpty())
8837 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8838 else
8839 rc = mParent->findRemoveableMedium(dev.deviceType,
8840 dev.uuid,
8841 false /* fRefresh */,
8842 false /* aSetError */,
8843 medium);
8844 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8845 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8846 rc = S_OK;
8847 break;
8848
8849 case DeviceType_HardDisk:
8850 {
8851 /* find a hard disk by UUID */
8852 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8853 if (FAILED(rc))
8854 {
8855 if (isSnapshotMachine())
8856 {
8857 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8858 // so the user knows that the bad disk is in a snapshot somewhere
8859 com::ErrorInfo info;
8860 return setError(E_FAIL,
8861 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8862 puuidSnapshot->raw(),
8863 info.getText().raw());
8864 }
8865 else
8866 return rc;
8867 }
8868
8869 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8870
8871 if (medium->getType() == MediumType_Immutable)
8872 {
8873 if (isSnapshotMachine())
8874 return setError(E_FAIL,
8875 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8876 "of the virtual machine '%s' ('%s')"),
8877 medium->getLocationFull().c_str(),
8878 dev.uuid.raw(),
8879 puuidSnapshot->raw(),
8880 mUserData->s.strName.c_str(),
8881 mData->m_strConfigFileFull.c_str());
8882
8883 return setError(E_FAIL,
8884 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8885 medium->getLocationFull().c_str(),
8886 dev.uuid.raw(),
8887 mUserData->s.strName.c_str(),
8888 mData->m_strConfigFileFull.c_str());
8889 }
8890
8891 if (medium->getType() == MediumType_MultiAttach)
8892 {
8893 if (isSnapshotMachine())
8894 return setError(E_FAIL,
8895 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8896 "of the virtual machine '%s' ('%s')"),
8897 medium->getLocationFull().c_str(),
8898 dev.uuid.raw(),
8899 puuidSnapshot->raw(),
8900 mUserData->s.strName.c_str(),
8901 mData->m_strConfigFileFull.c_str());
8902
8903 return setError(E_FAIL,
8904 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8905 medium->getLocationFull().c_str(),
8906 dev.uuid.raw(),
8907 mUserData->s.strName.c_str(),
8908 mData->m_strConfigFileFull.c_str());
8909 }
8910
8911 if ( !isSnapshotMachine()
8912 && medium->getChildren().size() != 0
8913 )
8914 return setError(E_FAIL,
8915 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8916 "because it has %d differencing child hard disks"),
8917 medium->getLocationFull().c_str(),
8918 dev.uuid.raw(),
8919 mUserData->s.strName.c_str(),
8920 mData->m_strConfigFileFull.c_str(),
8921 medium->getChildren().size());
8922
8923 if (findAttachment(mMediaData->mAttachments,
8924 medium))
8925 return setError(E_FAIL,
8926 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8927 medium->getLocationFull().c_str(),
8928 dev.uuid.raw(),
8929 mUserData->s.strName.c_str(),
8930 mData->m_strConfigFileFull.c_str());
8931
8932 break;
8933 }
8934
8935 default:
8936 return setError(E_FAIL,
8937 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8938 medium->getLocationFull().c_str(),
8939 mUserData->s.strName.c_str(),
8940 mData->m_strConfigFileFull.c_str());
8941 }
8942
8943 if (FAILED(rc))
8944 break;
8945
8946 /* Bandwidth groups are loaded at this point. */
8947 ComObjPtr<BandwidthGroup> pBwGroup;
8948
8949 if (!dev.strBwGroup.isEmpty())
8950 {
8951 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8952 if (FAILED(rc))
8953 return setError(E_FAIL,
8954 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8955 medium->getLocationFull().c_str(),
8956 dev.strBwGroup.c_str(),
8957 mUserData->s.strName.c_str(),
8958 mData->m_strConfigFileFull.c_str());
8959 pBwGroup->reference();
8960 }
8961
8962 const Bstr controllerName = aStorageController->getName();
8963 ComObjPtr<MediumAttachment> pAttachment;
8964 pAttachment.createObject();
8965 rc = pAttachment->init(this,
8966 medium,
8967 controllerName,
8968 dev.lPort,
8969 dev.lDevice,
8970 dev.deviceType,
8971 false,
8972 dev.fPassThrough,
8973 dev.fTempEject,
8974 dev.fNonRotational,
8975 dev.fDiscard,
8976 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8977 if (FAILED(rc)) break;
8978
8979 /* associate the medium with this machine and snapshot */
8980 if (!medium.isNull())
8981 {
8982 AutoCaller medCaller(medium);
8983 if (FAILED(medCaller.rc())) return medCaller.rc();
8984 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8985
8986 if (isSnapshotMachine())
8987 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8988 else
8989 rc = medium->addBackReference(mData->mUuid);
8990 /* If the medium->addBackReference fails it sets an appropriate
8991 * error message, so no need to do any guesswork here. */
8992
8993 if (puuidRegistry)
8994 // caller wants registry ID to be set on all attached media (OVF import case)
8995 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8996 }
8997
8998 if (FAILED(rc))
8999 break;
9000
9001 /* back up mMediaData to let registeredInit() properly rollback on failure
9002 * (= limited accessibility) */
9003 setModified(IsModified_Storage);
9004 mMediaData.backup();
9005 mMediaData->mAttachments.push_back(pAttachment);
9006 }
9007
9008 return rc;
9009}
9010
9011/**
9012 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9013 *
9014 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9015 * @param aSnapshot where to return the found snapshot
9016 * @param aSetError true to set extended error info on failure
9017 */
9018HRESULT Machine::findSnapshotById(const Guid &aId,
9019 ComObjPtr<Snapshot> &aSnapshot,
9020 bool aSetError /* = false */)
9021{
9022 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9023
9024 if (!mData->mFirstSnapshot)
9025 {
9026 if (aSetError)
9027 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9028 return E_FAIL;
9029 }
9030
9031 if (aId.isEmpty())
9032 aSnapshot = mData->mFirstSnapshot;
9033 else
9034 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9035
9036 if (!aSnapshot)
9037 {
9038 if (aSetError)
9039 return setError(E_FAIL,
9040 tr("Could not find a snapshot with UUID {%s}"),
9041 aId.toString().c_str());
9042 return E_FAIL;
9043 }
9044
9045 return S_OK;
9046}
9047
9048/**
9049 * Returns the snapshot with the given name or fails of no such snapshot.
9050 *
9051 * @param aName snapshot name to find
9052 * @param aSnapshot where to return the found snapshot
9053 * @param aSetError true to set extended error info on failure
9054 */
9055HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9056 ComObjPtr<Snapshot> &aSnapshot,
9057 bool aSetError /* = false */)
9058{
9059 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9060
9061 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9062
9063 if (!mData->mFirstSnapshot)
9064 {
9065 if (aSetError)
9066 return setError(VBOX_E_OBJECT_NOT_FOUND,
9067 tr("This machine does not have any snapshots"));
9068 return VBOX_E_OBJECT_NOT_FOUND;
9069 }
9070
9071 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9072
9073 if (!aSnapshot)
9074 {
9075 if (aSetError)
9076 return setError(VBOX_E_OBJECT_NOT_FOUND,
9077 tr("Could not find a snapshot named '%s'"), strName.c_str());
9078 return VBOX_E_OBJECT_NOT_FOUND;
9079 }
9080
9081 return S_OK;
9082}
9083
9084/**
9085 * Returns a storage controller object with the given name.
9086 *
9087 * @param aName storage controller name to find
9088 * @param aStorageController where to return the found storage controller
9089 * @param aSetError true to set extended error info on failure
9090 */
9091HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9092 ComObjPtr<StorageController> &aStorageController,
9093 bool aSetError /* = false */)
9094{
9095 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9096
9097 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9098 it != mStorageControllers->end();
9099 ++it)
9100 {
9101 if ((*it)->getName() == aName)
9102 {
9103 aStorageController = (*it);
9104 return S_OK;
9105 }
9106 }
9107
9108 if (aSetError)
9109 return setError(VBOX_E_OBJECT_NOT_FOUND,
9110 tr("Could not find a storage controller named '%s'"),
9111 aName.c_str());
9112 return VBOX_E_OBJECT_NOT_FOUND;
9113}
9114
9115HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9116 MediaData::AttachmentList &atts)
9117{
9118 AutoCaller autoCaller(this);
9119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9120
9121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9122
9123 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9124 it != mMediaData->mAttachments.end();
9125 ++it)
9126 {
9127 const ComObjPtr<MediumAttachment> &pAtt = *it;
9128
9129 // should never happen, but deal with NULL pointers in the list.
9130 AssertStmt(!pAtt.isNull(), continue);
9131
9132 // getControllerName() needs caller+read lock
9133 AutoCaller autoAttCaller(pAtt);
9134 if (FAILED(autoAttCaller.rc()))
9135 {
9136 atts.clear();
9137 return autoAttCaller.rc();
9138 }
9139 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9140
9141 if (pAtt->getControllerName() == aName)
9142 atts.push_back(pAtt);
9143 }
9144
9145 return S_OK;
9146}
9147
9148/**
9149 * Helper for #saveSettings. Cares about renaming the settings directory and
9150 * file if the machine name was changed and about creating a new settings file
9151 * if this is a new machine.
9152 *
9153 * @note Must be never called directly but only from #saveSettings().
9154 */
9155HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9156{
9157 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9158
9159 HRESULT rc = S_OK;
9160
9161 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9162
9163 /// @todo need to handle primary group change, too
9164
9165 /* attempt to rename the settings file if machine name is changed */
9166 if ( mUserData->s.fNameSync
9167 && mUserData.isBackedUp()
9168 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9169 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9170 )
9171 {
9172 bool dirRenamed = false;
9173 bool fileRenamed = false;
9174
9175 Utf8Str configFile, newConfigFile;
9176 Utf8Str configFilePrev, newConfigFilePrev;
9177 Utf8Str configDir, newConfigDir;
9178
9179 do
9180 {
9181 int vrc = VINF_SUCCESS;
9182
9183 Utf8Str name = mUserData.backedUpData()->s.strName;
9184 Utf8Str newName = mUserData->s.strName;
9185 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9186 if (group == "/")
9187 group.setNull();
9188 Utf8Str newGroup = mUserData->s.llGroups.front();
9189 if (newGroup == "/")
9190 newGroup.setNull();
9191
9192 configFile = mData->m_strConfigFileFull;
9193
9194 /* first, rename the directory if it matches the group and machine name */
9195 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9196 group.c_str(), RTPATH_DELIMITER, name.c_str());
9197 /** @todo hack, make somehow use of ComposeMachineFilename */
9198 if (mUserData->s.fDirectoryIncludesUUID)
9199 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9200 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9201 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9202 /** @todo hack, make somehow use of ComposeMachineFilename */
9203 if (mUserData->s.fDirectoryIncludesUUID)
9204 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9205 configDir = configFile;
9206 configDir.stripFilename();
9207 newConfigDir = configDir;
9208 if ( configDir.length() >= groupPlusName.length()
9209 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9210 {
9211 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9212 Utf8Str newConfigBaseDir(newConfigDir);
9213 newConfigDir.append(newGroupPlusName);
9214 /* consistency: use \ if appropriate on the platform */
9215 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9216 /* new dir and old dir cannot be equal here because of 'if'
9217 * above and because name != newName */
9218 Assert(configDir != newConfigDir);
9219 if (!fSettingsFileIsNew)
9220 {
9221 /* perform real rename only if the machine is not new */
9222 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9223 if ( vrc == VERR_FILE_NOT_FOUND
9224 || vrc == VERR_PATH_NOT_FOUND)
9225 {
9226 /* create the parent directory, then retry renaming */
9227 Utf8Str parent(newConfigDir);
9228 parent.stripFilename();
9229 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9230 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9231 }
9232 if (RT_FAILURE(vrc))
9233 {
9234 rc = setError(E_FAIL,
9235 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9236 configDir.c_str(),
9237 newConfigDir.c_str(),
9238 vrc);
9239 break;
9240 }
9241 /* delete subdirectories which are no longer needed */
9242 Utf8Str dir(configDir);
9243 dir.stripFilename();
9244 while (dir != newConfigBaseDir && dir != ".")
9245 {
9246 vrc = RTDirRemove(dir.c_str());
9247 if (RT_FAILURE(vrc))
9248 break;
9249 dir.stripFilename();
9250 }
9251 dirRenamed = true;
9252 }
9253 }
9254
9255 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9256 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9257
9258 /* then try to rename the settings file itself */
9259 if (newConfigFile != configFile)
9260 {
9261 /* get the path to old settings file in renamed directory */
9262 configFile = Utf8StrFmt("%s%c%s",
9263 newConfigDir.c_str(),
9264 RTPATH_DELIMITER,
9265 RTPathFilename(configFile.c_str()));
9266 if (!fSettingsFileIsNew)
9267 {
9268 /* perform real rename only if the machine is not new */
9269 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9270 if (RT_FAILURE(vrc))
9271 {
9272 rc = setError(E_FAIL,
9273 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9274 configFile.c_str(),
9275 newConfigFile.c_str(),
9276 vrc);
9277 break;
9278 }
9279 fileRenamed = true;
9280 configFilePrev = configFile;
9281 configFilePrev += "-prev";
9282 newConfigFilePrev = newConfigFile;
9283 newConfigFilePrev += "-prev";
9284 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9285 }
9286 }
9287
9288 // update m_strConfigFileFull amd mConfigFile
9289 mData->m_strConfigFileFull = newConfigFile;
9290 // compute the relative path too
9291 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9292
9293 // store the old and new so that VirtualBox::saveSettings() can update
9294 // the media registry
9295 if ( mData->mRegistered
9296 && configDir != newConfigDir)
9297 {
9298 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9299
9300 if (pfNeedsGlobalSaveSettings)
9301 *pfNeedsGlobalSaveSettings = true;
9302 }
9303
9304 // in the saved state file path, replace the old directory with the new directory
9305 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9306 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9307
9308 // and do the same thing for the saved state file paths of all the online snapshots
9309 if (mData->mFirstSnapshot)
9310 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9311 newConfigDir.c_str());
9312 }
9313 while (0);
9314
9315 if (FAILED(rc))
9316 {
9317 /* silently try to rename everything back */
9318 if (fileRenamed)
9319 {
9320 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9321 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9322 }
9323 if (dirRenamed)
9324 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9325 }
9326
9327 if (FAILED(rc)) return rc;
9328 }
9329
9330 if (fSettingsFileIsNew)
9331 {
9332 /* create a virgin config file */
9333 int vrc = VINF_SUCCESS;
9334
9335 /* ensure the settings directory exists */
9336 Utf8Str path(mData->m_strConfigFileFull);
9337 path.stripFilename();
9338 if (!RTDirExists(path.c_str()))
9339 {
9340 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9341 if (RT_FAILURE(vrc))
9342 {
9343 return setError(E_FAIL,
9344 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9345 path.c_str(),
9346 vrc);
9347 }
9348 }
9349
9350 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9351 path = Utf8Str(mData->m_strConfigFileFull);
9352 RTFILE f = NIL_RTFILE;
9353 vrc = RTFileOpen(&f, path.c_str(),
9354 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9355 if (RT_FAILURE(vrc))
9356 return setError(E_FAIL,
9357 tr("Could not create the settings file '%s' (%Rrc)"),
9358 path.c_str(),
9359 vrc);
9360 RTFileClose(f);
9361 }
9362
9363 return rc;
9364}
9365
9366/**
9367 * Saves and commits machine data, user data and hardware data.
9368 *
9369 * Note that on failure, the data remains uncommitted.
9370 *
9371 * @a aFlags may combine the following flags:
9372 *
9373 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9374 * Used when saving settings after an operation that makes them 100%
9375 * correspond to the settings from the current snapshot.
9376 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9377 * #isReallyModified() returns false. This is necessary for cases when we
9378 * change machine data directly, not through the backup()/commit() mechanism.
9379 * - SaveS_Force: settings will be saved without doing a deep compare of the
9380 * settings structures. This is used when this is called because snapshots
9381 * have changed to avoid the overhead of the deep compare.
9382 *
9383 * @note Must be called from under this object's write lock. Locks children for
9384 * writing.
9385 *
9386 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9387 * initialized to false and that will be set to true by this function if
9388 * the caller must invoke VirtualBox::saveSettings() because the global
9389 * settings have changed. This will happen if a machine rename has been
9390 * saved and the global machine and media registries will therefore need
9391 * updating.
9392 */
9393HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9394 int aFlags /*= 0*/)
9395{
9396 LogFlowThisFuncEnter();
9397
9398 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9399
9400 /* make sure child objects are unable to modify the settings while we are
9401 * saving them */
9402 ensureNoStateDependencies();
9403
9404 AssertReturn(!isSnapshotMachine(),
9405 E_FAIL);
9406
9407 HRESULT rc = S_OK;
9408 bool fNeedsWrite = false;
9409
9410 /* First, prepare to save settings. It will care about renaming the
9411 * settings directory and file if the machine name was changed and about
9412 * creating a new settings file if this is a new machine. */
9413 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9414 if (FAILED(rc)) return rc;
9415
9416 // keep a pointer to the current settings structures
9417 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9418 settings::MachineConfigFile *pNewConfig = NULL;
9419
9420 try
9421 {
9422 // make a fresh one to have everyone write stuff into
9423 pNewConfig = new settings::MachineConfigFile(NULL);
9424 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9425
9426 // now go and copy all the settings data from COM to the settings structures
9427 // (this calles saveSettings() on all the COM objects in the machine)
9428 copyMachineDataToSettings(*pNewConfig);
9429
9430 if (aFlags & SaveS_ResetCurStateModified)
9431 {
9432 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9433 mData->mCurrentStateModified = FALSE;
9434 fNeedsWrite = true; // always, no need to compare
9435 }
9436 else if (aFlags & SaveS_Force)
9437 {
9438 fNeedsWrite = true; // always, no need to compare
9439 }
9440 else
9441 {
9442 if (!mData->mCurrentStateModified)
9443 {
9444 // do a deep compare of the settings that we just saved with the settings
9445 // previously stored in the config file; this invokes MachineConfigFile::operator==
9446 // which does a deep compare of all the settings, which is expensive but less expensive
9447 // than writing out XML in vain
9448 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9449
9450 // could still be modified if any settings changed
9451 mData->mCurrentStateModified = fAnySettingsChanged;
9452
9453 fNeedsWrite = fAnySettingsChanged;
9454 }
9455 else
9456 fNeedsWrite = true;
9457 }
9458
9459 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9460
9461 if (fNeedsWrite)
9462 // now spit it all out!
9463 pNewConfig->write(mData->m_strConfigFileFull);
9464
9465 mData->pMachineConfigFile = pNewConfig;
9466 delete pOldConfig;
9467 commit();
9468
9469 // after saving settings, we are no longer different from the XML on disk
9470 mData->flModifications = 0;
9471 }
9472 catch (HRESULT err)
9473 {
9474 // we assume that error info is set by the thrower
9475 rc = err;
9476
9477 // restore old config
9478 delete pNewConfig;
9479 mData->pMachineConfigFile = pOldConfig;
9480 }
9481 catch (...)
9482 {
9483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9484 }
9485
9486 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9487 {
9488 /* Fire the data change event, even on failure (since we've already
9489 * committed all data). This is done only for SessionMachines because
9490 * mutable Machine instances are always not registered (i.e. private
9491 * to the client process that creates them) and thus don't need to
9492 * inform callbacks. */
9493 if (isSessionMachine())
9494 mParent->onMachineDataChange(mData->mUuid);
9495 }
9496
9497 LogFlowThisFunc(("rc=%08X\n", rc));
9498 LogFlowThisFuncLeave();
9499 return rc;
9500}
9501
9502/**
9503 * Implementation for saving the machine settings into the given
9504 * settings::MachineConfigFile instance. This copies machine extradata
9505 * from the previous machine config file in the instance data, if any.
9506 *
9507 * This gets called from two locations:
9508 *
9509 * -- Machine::saveSettings(), during the regular XML writing;
9510 *
9511 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9512 * exported to OVF and we write the VirtualBox proprietary XML
9513 * into a <vbox:Machine> tag.
9514 *
9515 * This routine fills all the fields in there, including snapshots, *except*
9516 * for the following:
9517 *
9518 * -- fCurrentStateModified. There is some special logic associated with that.
9519 *
9520 * The caller can then call MachineConfigFile::write() or do something else
9521 * with it.
9522 *
9523 * Caller must hold the machine lock!
9524 *
9525 * This throws XML errors and HRESULT, so the caller must have a catch block!
9526 */
9527void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9528{
9529 // deep copy extradata
9530 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9531
9532 config.uuid = mData->mUuid;
9533
9534 // copy name, description, OS type, teleport, UTC etc.
9535 config.machineUserData = mUserData->s;
9536
9537 if ( mData->mMachineState == MachineState_Saved
9538 || mData->mMachineState == MachineState_Restoring
9539 // when deleting a snapshot we may or may not have a saved state in the current state,
9540 // so let's not assert here please
9541 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9542 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9543 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9544 && (!mSSData->strStateFilePath.isEmpty())
9545 )
9546 )
9547 {
9548 Assert(!mSSData->strStateFilePath.isEmpty());
9549 /* try to make the file name relative to the settings file dir */
9550 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9551 }
9552 else
9553 {
9554 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9555 config.strStateFile.setNull();
9556 }
9557
9558 if (mData->mCurrentSnapshot)
9559 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9560 else
9561 config.uuidCurrentSnapshot.clear();
9562
9563 config.timeLastStateChange = mData->mLastStateChange;
9564 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9565 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9566
9567 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9568 if (FAILED(rc)) throw rc;
9569
9570 rc = saveStorageControllers(config.storageMachine);
9571 if (FAILED(rc)) throw rc;
9572
9573 // save machine's media registry if this is VirtualBox 4.0 or later
9574 if (config.canHaveOwnMediaRegistry())
9575 {
9576 // determine machine folder
9577 Utf8Str strMachineFolder = getSettingsFileFull();
9578 strMachineFolder.stripFilename();
9579 mParent->saveMediaRegistry(config.mediaRegistry,
9580 getId(), // only media with registry ID == machine UUID
9581 strMachineFolder);
9582 // this throws HRESULT
9583 }
9584
9585 // save snapshots
9586 rc = saveAllSnapshots(config);
9587 if (FAILED(rc)) throw rc;
9588}
9589
9590/**
9591 * Saves all snapshots of the machine into the given machine config file. Called
9592 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9593 * @param config
9594 * @return
9595 */
9596HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9597{
9598 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9599
9600 HRESULT rc = S_OK;
9601
9602 try
9603 {
9604 config.llFirstSnapshot.clear();
9605
9606 if (mData->mFirstSnapshot)
9607 {
9608 settings::Snapshot snapNew;
9609 config.llFirstSnapshot.push_back(snapNew);
9610
9611 // get reference to the fresh copy of the snapshot on the list and
9612 // work on that copy directly to avoid excessive copying later
9613 settings::Snapshot &snap = config.llFirstSnapshot.front();
9614
9615 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9616 if (FAILED(rc)) throw rc;
9617 }
9618
9619// if (mType == IsSessionMachine)
9620// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9621
9622 }
9623 catch (HRESULT err)
9624 {
9625 /* we assume that error info is set by the thrower */
9626 rc = err;
9627 }
9628 catch (...)
9629 {
9630 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9631 }
9632
9633 return rc;
9634}
9635
9636/**
9637 * Saves the VM hardware configuration. It is assumed that the
9638 * given node is empty.
9639 *
9640 * @param data Reference to the settings object for the hardware config.
9641 * @param pDbg Pointer to the settings object for the debugging config
9642 * which happens to live in mHWData.
9643 * @param pAutostart Pointer to the settings object for the autostart config
9644 * which happens to live in mHWData.
9645 */
9646HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9647 settings::Autostart *pAutostart)
9648{
9649 HRESULT rc = S_OK;
9650
9651 try
9652 {
9653 /* The hardware version attribute (optional).
9654 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9655 if ( mHWData->mHWVersion == "1"
9656 && mSSData->strStateFilePath.isEmpty()
9657 )
9658 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. */
9659
9660 data.strVersion = mHWData->mHWVersion;
9661 data.uuid = mHWData->mHardwareUUID;
9662
9663 // CPU
9664 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9665 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9666 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9667 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9668 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9669 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9670 data.fPAE = !!mHWData->mPAEEnabled;
9671 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9672
9673 /* Standard and Extended CPUID leafs. */
9674 data.llCpuIdLeafs.clear();
9675 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9676 {
9677 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9678 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9679 }
9680 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9681 {
9682 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9683 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9684 }
9685
9686 data.cCPUs = mHWData->mCPUCount;
9687 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9688 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9689
9690 data.llCpus.clear();
9691 if (data.fCpuHotPlug)
9692 {
9693 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9694 {
9695 if (mHWData->mCPUAttached[idx])
9696 {
9697 settings::Cpu cpu;
9698 cpu.ulId = idx;
9699 data.llCpus.push_back(cpu);
9700 }
9701 }
9702 }
9703
9704 // memory
9705 data.ulMemorySizeMB = mHWData->mMemorySize;
9706 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9707
9708 // firmware
9709 data.firmwareType = mHWData->mFirmwareType;
9710
9711 // HID
9712 data.pointingHIDType = mHWData->mPointingHIDType;
9713 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9714
9715 // chipset
9716 data.chipsetType = mHWData->mChipsetType;
9717
9718 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9719
9720 // HPET
9721 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9722
9723 // boot order
9724 data.mapBootOrder.clear();
9725 for (size_t i = 0;
9726 i < RT_ELEMENTS(mHWData->mBootOrder);
9727 ++i)
9728 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9729
9730 // display
9731 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9732 data.cMonitors = mHWData->mMonitorCount;
9733 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9734 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9735 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9736 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9737 data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled;
9738 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9739
9740 /* VRDEServer settings (optional) */
9741 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9742 if (FAILED(rc)) throw rc;
9743
9744 /* BIOS (required) */
9745 rc = mBIOSSettings->saveSettings(data.biosSettings);
9746 if (FAILED(rc)) throw rc;
9747
9748 /* USB Controller (required) */
9749 rc = mUSBController->saveSettings(data.usbController);
9750 if (FAILED(rc)) throw rc;
9751
9752 /* Network adapters (required) */
9753 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9754 data.llNetworkAdapters.clear();
9755 /* Write out only the nominal number of network adapters for this
9756 * chipset type. Since Machine::commit() hasn't been called there
9757 * may be extra NIC settings in the vector. */
9758 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9759 {
9760 settings::NetworkAdapter nic;
9761 nic.ulSlot = slot;
9762 /* paranoia check... must not be NULL, but must not crash either. */
9763 if (mNetworkAdapters[slot])
9764 {
9765 rc = mNetworkAdapters[slot]->saveSettings(nic);
9766 if (FAILED(rc)) throw rc;
9767
9768 data.llNetworkAdapters.push_back(nic);
9769 }
9770 }
9771
9772 /* Serial ports */
9773 data.llSerialPorts.clear();
9774 for (ULONG slot = 0;
9775 slot < RT_ELEMENTS(mSerialPorts);
9776 ++slot)
9777 {
9778 settings::SerialPort s;
9779 s.ulSlot = slot;
9780 rc = mSerialPorts[slot]->saveSettings(s);
9781 if (FAILED(rc)) return rc;
9782
9783 data.llSerialPorts.push_back(s);
9784 }
9785
9786 /* Parallel ports */
9787 data.llParallelPorts.clear();
9788 for (ULONG slot = 0;
9789 slot < RT_ELEMENTS(mParallelPorts);
9790 ++slot)
9791 {
9792 settings::ParallelPort p;
9793 p.ulSlot = slot;
9794 rc = mParallelPorts[slot]->saveSettings(p);
9795 if (FAILED(rc)) return rc;
9796
9797 data.llParallelPorts.push_back(p);
9798 }
9799
9800 /* Audio adapter */
9801 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9802 if (FAILED(rc)) return rc;
9803
9804 /* Shared folders */
9805 data.llSharedFolders.clear();
9806 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9807 it != mHWData->mSharedFolders.end();
9808 ++it)
9809 {
9810 SharedFolder *pSF = *it;
9811 AutoCaller sfCaller(pSF);
9812 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9813 settings::SharedFolder sf;
9814 sf.strName = pSF->getName();
9815 sf.strHostPath = pSF->getHostPath();
9816 sf.fWritable = !!pSF->isWritable();
9817 sf.fAutoMount = !!pSF->isAutoMounted();
9818
9819 data.llSharedFolders.push_back(sf);
9820 }
9821
9822 // clipboard
9823 data.clipboardMode = mHWData->mClipboardMode;
9824
9825 // drag'n'drop
9826 data.dragAndDropMode = mHWData->mDragAndDropMode;
9827
9828 /* Guest */
9829 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9830
9831 // IO settings
9832 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9833 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9834
9835 /* BandwidthControl (required) */
9836 rc = mBandwidthControl->saveSettings(data.ioSettings);
9837 if (FAILED(rc)) throw rc;
9838
9839 /* Host PCI devices */
9840 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9841 it != mHWData->mPCIDeviceAssignments.end();
9842 ++it)
9843 {
9844 ComObjPtr<PCIDeviceAttachment> pda = *it;
9845 settings::HostPCIDeviceAttachment hpda;
9846
9847 rc = pda->saveSettings(hpda);
9848 if (FAILED(rc)) throw rc;
9849
9850 data.pciAttachments.push_back(hpda);
9851 }
9852
9853
9854 // guest properties
9855 data.llGuestProperties.clear();
9856#ifdef VBOX_WITH_GUEST_PROPS
9857 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9858 it != mHWData->mGuestProperties.end();
9859 ++it)
9860 {
9861 HWData::GuestProperty property = *it;
9862
9863 /* Remove transient guest properties at shutdown unless we
9864 * are saving state */
9865 if ( ( mData->mMachineState == MachineState_PoweredOff
9866 || mData->mMachineState == MachineState_Aborted
9867 || mData->mMachineState == MachineState_Teleported)
9868 && ( property.mFlags & guestProp::TRANSIENT
9869 || property.mFlags & guestProp::TRANSRESET))
9870 continue;
9871 settings::GuestProperty prop;
9872 prop.strName = property.strName;
9873 prop.strValue = property.strValue;
9874 prop.timestamp = property.mTimestamp;
9875 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9876 guestProp::writeFlags(property.mFlags, szFlags);
9877 prop.strFlags = szFlags;
9878
9879 data.llGuestProperties.push_back(prop);
9880 }
9881
9882 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9883 /* I presume this doesn't require a backup(). */
9884 mData->mGuestPropertiesModified = FALSE;
9885#endif /* VBOX_WITH_GUEST_PROPS defined */
9886
9887 *pDbg = mHWData->mDebugging;
9888 *pAutostart = mHWData->mAutostart;
9889 }
9890 catch(std::bad_alloc &)
9891 {
9892 return E_OUTOFMEMORY;
9893 }
9894
9895 AssertComRC(rc);
9896 return rc;
9897}
9898
9899/**
9900 * Saves the storage controller configuration.
9901 *
9902 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9903 */
9904HRESULT Machine::saveStorageControllers(settings::Storage &data)
9905{
9906 data.llStorageControllers.clear();
9907
9908 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9909 it != mStorageControllers->end();
9910 ++it)
9911 {
9912 HRESULT rc;
9913 ComObjPtr<StorageController> pCtl = *it;
9914
9915 settings::StorageController ctl;
9916 ctl.strName = pCtl->getName();
9917 ctl.controllerType = pCtl->getControllerType();
9918 ctl.storageBus = pCtl->getStorageBus();
9919 ctl.ulInstance = pCtl->getInstance();
9920 ctl.fBootable = pCtl->getBootable();
9921
9922 /* Save the port count. */
9923 ULONG portCount;
9924 rc = pCtl->COMGETTER(PortCount)(&portCount);
9925 ComAssertComRCRet(rc, rc);
9926 ctl.ulPortCount = portCount;
9927
9928 /* Save fUseHostIOCache */
9929 BOOL fUseHostIOCache;
9930 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9931 ComAssertComRCRet(rc, rc);
9932 ctl.fUseHostIOCache = !!fUseHostIOCache;
9933
9934 /* Save IDE emulation settings. */
9935 if (ctl.controllerType == StorageControllerType_IntelAhci)
9936 {
9937 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9938 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9939 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9940 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9941 )
9942 ComAssertComRCRet(rc, rc);
9943 }
9944
9945 /* save the devices now. */
9946 rc = saveStorageDevices(pCtl, ctl);
9947 ComAssertComRCRet(rc, rc);
9948
9949 data.llStorageControllers.push_back(ctl);
9950 }
9951
9952 return S_OK;
9953}
9954
9955/**
9956 * Saves the hard disk configuration.
9957 */
9958HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9959 settings::StorageController &data)
9960{
9961 MediaData::AttachmentList atts;
9962
9963 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9964 if (FAILED(rc)) return rc;
9965
9966 data.llAttachedDevices.clear();
9967 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9968 it != atts.end();
9969 ++it)
9970 {
9971 settings::AttachedDevice dev;
9972
9973 MediumAttachment *pAttach = *it;
9974 Medium *pMedium = pAttach->getMedium();
9975
9976 dev.deviceType = pAttach->getType();
9977 dev.lPort = pAttach->getPort();
9978 dev.lDevice = pAttach->getDevice();
9979 if (pMedium)
9980 {
9981 if (pMedium->isHostDrive())
9982 dev.strHostDriveSrc = pMedium->getLocationFull();
9983 else
9984 dev.uuid = pMedium->getId();
9985 dev.fPassThrough = pAttach->getPassthrough();
9986 dev.fTempEject = pAttach->getTempEject();
9987 dev.fNonRotational = pAttach->getNonRotational();
9988 dev.fDiscard = pAttach->getDiscard();
9989 }
9990
9991 dev.strBwGroup = pAttach->getBandwidthGroup();
9992
9993 data.llAttachedDevices.push_back(dev);
9994 }
9995
9996 return S_OK;
9997}
9998
9999/**
10000 * Saves machine state settings as defined by aFlags
10001 * (SaveSTS_* values).
10002 *
10003 * @param aFlags Combination of SaveSTS_* flags.
10004 *
10005 * @note Locks objects for writing.
10006 */
10007HRESULT Machine::saveStateSettings(int aFlags)
10008{
10009 if (aFlags == 0)
10010 return S_OK;
10011
10012 AutoCaller autoCaller(this);
10013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10014
10015 /* This object's write lock is also necessary to serialize file access
10016 * (prevent concurrent reads and writes) */
10017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10018
10019 HRESULT rc = S_OK;
10020
10021 Assert(mData->pMachineConfigFile);
10022
10023 try
10024 {
10025 if (aFlags & SaveSTS_CurStateModified)
10026 mData->pMachineConfigFile->fCurrentStateModified = true;
10027
10028 if (aFlags & SaveSTS_StateFilePath)
10029 {
10030 if (!mSSData->strStateFilePath.isEmpty())
10031 /* try to make the file name relative to the settings file dir */
10032 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10033 else
10034 mData->pMachineConfigFile->strStateFile.setNull();
10035 }
10036
10037 if (aFlags & SaveSTS_StateTimeStamp)
10038 {
10039 Assert( mData->mMachineState != MachineState_Aborted
10040 || mSSData->strStateFilePath.isEmpty());
10041
10042 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10043
10044 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10045//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10046 }
10047
10048 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10049 }
10050 catch (...)
10051 {
10052 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10053 }
10054
10055 return rc;
10056}
10057
10058/**
10059 * Ensures that the given medium is added to a media registry. If this machine
10060 * was created with 4.0 or later, then the machine registry is used. Otherwise
10061 * the global VirtualBox media registry is used.
10062 *
10063 * Caller must NOT hold machine lock, media tree or any medium locks!
10064 *
10065 * @param pMedium
10066 */
10067void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10068{
10069 /* Paranoia checks: do not hold machine or media tree locks. */
10070 AssertReturnVoid(!isWriteLockOnCurrentThread());
10071 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10072
10073 ComObjPtr<Medium> pBase;
10074 {
10075 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10076 pBase = pMedium->getBase();
10077 }
10078
10079 /* Paranoia checks: do not hold medium locks. */
10080 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10081 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10082
10083 // decide which medium registry to use now that the medium is attached:
10084 Guid uuid;
10085 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10086 // machine XML is VirtualBox 4.0 or higher:
10087 uuid = getId(); // machine UUID
10088 else
10089 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10090
10091 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10092 mParent->markRegistryModified(uuid);
10093
10094 /* For more complex hard disk structures it can happen that the base
10095 * medium isn't yet associated with any medium registry. Do that now. */
10096 if (pMedium != pBase)
10097 {
10098 if (pBase->addRegistry(uuid, true /* fRecurse */))
10099 mParent->markRegistryModified(uuid);
10100 }
10101}
10102
10103/**
10104 * Creates differencing hard disks for all normal hard disks attached to this
10105 * machine and a new set of attachments to refer to created disks.
10106 *
10107 * Used when taking a snapshot or when deleting the current state. Gets called
10108 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10109 *
10110 * This method assumes that mMediaData contains the original hard disk attachments
10111 * it needs to create diffs for. On success, these attachments will be replaced
10112 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10113 * called to delete created diffs which will also rollback mMediaData and restore
10114 * whatever was backed up before calling this method.
10115 *
10116 * Attachments with non-normal hard disks are left as is.
10117 *
10118 * If @a aOnline is @c false then the original hard disks that require implicit
10119 * diffs will be locked for reading. Otherwise it is assumed that they are
10120 * already locked for writing (when the VM was started). Note that in the latter
10121 * case it is responsibility of the caller to lock the newly created diffs for
10122 * writing if this method succeeds.
10123 *
10124 * @param aProgress Progress object to run (must contain at least as
10125 * many operations left as the number of hard disks
10126 * attached).
10127 * @param aOnline Whether the VM was online prior to this operation.
10128 *
10129 * @note The progress object is not marked as completed, neither on success nor
10130 * on failure. This is a responsibility of the caller.
10131 *
10132 * @note Locks this object and the media tree for writing.
10133 */
10134HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10135 ULONG aWeight,
10136 bool aOnline)
10137{
10138 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10139
10140 AutoCaller autoCaller(this);
10141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10142
10143 AutoMultiWriteLock2 alock(this->lockHandle(),
10144 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10145
10146 /* must be in a protective state because we release the lock below */
10147 AssertReturn( mData->mMachineState == MachineState_Saving
10148 || mData->mMachineState == MachineState_LiveSnapshotting
10149 || mData->mMachineState == MachineState_RestoringSnapshot
10150 || mData->mMachineState == MachineState_DeletingSnapshot
10151 , E_FAIL);
10152
10153 HRESULT rc = S_OK;
10154
10155 // use appropriate locked media map (online or offline)
10156 MediumLockListMap lockedMediaOffline;
10157 MediumLockListMap *lockedMediaMap;
10158 if (aOnline)
10159 lockedMediaMap = &mData->mSession.mLockedMedia;
10160 else
10161 lockedMediaMap = &lockedMediaOffline;
10162
10163 try
10164 {
10165 if (!aOnline)
10166 {
10167 /* lock all attached hard disks early to detect "in use"
10168 * situations before creating actual diffs */
10169 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10170 it != mMediaData->mAttachments.end();
10171 ++it)
10172 {
10173 MediumAttachment* pAtt = *it;
10174 if (pAtt->getType() == DeviceType_HardDisk)
10175 {
10176 Medium* pMedium = pAtt->getMedium();
10177 Assert(pMedium);
10178
10179 MediumLockList *pMediumLockList(new MediumLockList());
10180 alock.release();
10181 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10182 false /* fMediumLockWrite */,
10183 NULL,
10184 *pMediumLockList);
10185 alock.acquire();
10186 if (FAILED(rc))
10187 {
10188 delete pMediumLockList;
10189 throw rc;
10190 }
10191 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10192 if (FAILED(rc))
10193 {
10194 throw setError(rc,
10195 tr("Collecting locking information for all attached media failed"));
10196 }
10197 }
10198 }
10199
10200 /* Now lock all media. If this fails, nothing is locked. */
10201 alock.release();
10202 rc = lockedMediaMap->Lock();
10203 alock.acquire();
10204 if (FAILED(rc))
10205 {
10206 throw setError(rc,
10207 tr("Locking of attached media failed"));
10208 }
10209 }
10210
10211 /* remember the current list (note that we don't use backup() since
10212 * mMediaData may be already backed up) */
10213 MediaData::AttachmentList atts = mMediaData->mAttachments;
10214
10215 /* start from scratch */
10216 mMediaData->mAttachments.clear();
10217
10218 /* go through remembered attachments and create diffs for normal hard
10219 * disks and attach them */
10220 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10221 it != atts.end();
10222 ++it)
10223 {
10224 MediumAttachment* pAtt = *it;
10225
10226 DeviceType_T devType = pAtt->getType();
10227 Medium* pMedium = pAtt->getMedium();
10228
10229 if ( devType != DeviceType_HardDisk
10230 || pMedium == NULL
10231 || pMedium->getType() != MediumType_Normal)
10232 {
10233 /* copy the attachment as is */
10234
10235 /** @todo the progress object created in Console::TakeSnaphot
10236 * only expects operations for hard disks. Later other
10237 * device types need to show up in the progress as well. */
10238 if (devType == DeviceType_HardDisk)
10239 {
10240 if (pMedium == NULL)
10241 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10242 aWeight); // weight
10243 else
10244 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10245 pMedium->getBase()->getName().c_str()).raw(),
10246 aWeight); // weight
10247 }
10248
10249 mMediaData->mAttachments.push_back(pAtt);
10250 continue;
10251 }
10252
10253 /* need a diff */
10254 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10255 pMedium->getBase()->getName().c_str()).raw(),
10256 aWeight); // weight
10257
10258 Utf8Str strFullSnapshotFolder;
10259 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10260
10261 ComObjPtr<Medium> diff;
10262 diff.createObject();
10263 // store the diff in the same registry as the parent
10264 // (this cannot fail here because we can't create implicit diffs for
10265 // unregistered images)
10266 Guid uuidRegistryParent;
10267 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10268 Assert(fInRegistry); NOREF(fInRegistry);
10269 rc = diff->init(mParent,
10270 pMedium->getPreferredDiffFormat(),
10271 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10272 uuidRegistryParent);
10273 if (FAILED(rc)) throw rc;
10274
10275 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10276 * the push_back? Looks like we're going to release medium with the
10277 * wrong kind of lock (general issue with if we fail anywhere at all)
10278 * and an orphaned VDI in the snapshots folder. */
10279
10280 /* update the appropriate lock list */
10281 MediumLockList *pMediumLockList;
10282 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10283 AssertComRCThrowRC(rc);
10284 if (aOnline)
10285 {
10286 alock.release();
10287 /* The currently attached medium will be read-only, change
10288 * the lock type to read. */
10289 rc = pMediumLockList->Update(pMedium, false);
10290 alock.acquire();
10291 AssertComRCThrowRC(rc);
10292 }
10293
10294 /* release the locks before the potentially lengthy operation */
10295 alock.release();
10296 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10297 pMediumLockList,
10298 NULL /* aProgress */,
10299 true /* aWait */);
10300 alock.acquire();
10301 if (FAILED(rc)) throw rc;
10302
10303 rc = lockedMediaMap->Unlock();
10304 AssertComRCThrowRC(rc);
10305 alock.release();
10306 rc = pMediumLockList->Append(diff, true);
10307 alock.acquire();
10308 AssertComRCThrowRC(rc);
10309 alock.release();
10310 rc = lockedMediaMap->Lock();
10311 alock.acquire();
10312 AssertComRCThrowRC(rc);
10313
10314 rc = diff->addBackReference(mData->mUuid);
10315 AssertComRCThrowRC(rc);
10316
10317 /* add a new attachment */
10318 ComObjPtr<MediumAttachment> attachment;
10319 attachment.createObject();
10320 rc = attachment->init(this,
10321 diff,
10322 pAtt->getControllerName(),
10323 pAtt->getPort(),
10324 pAtt->getDevice(),
10325 DeviceType_HardDisk,
10326 true /* aImplicit */,
10327 false /* aPassthrough */,
10328 false /* aTempEject */,
10329 pAtt->getNonRotational(),
10330 pAtt->getDiscard(),
10331 pAtt->getBandwidthGroup());
10332 if (FAILED(rc)) throw rc;
10333
10334 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10335 AssertComRCThrowRC(rc);
10336 mMediaData->mAttachments.push_back(attachment);
10337 }
10338 }
10339 catch (HRESULT aRC) { rc = aRC; }
10340
10341 /* unlock all hard disks we locked when there is no VM */
10342 if (!aOnline)
10343 {
10344 ErrorInfoKeeper eik;
10345
10346 HRESULT rc1 = lockedMediaMap->Clear();
10347 AssertComRC(rc1);
10348 }
10349
10350 return rc;
10351}
10352
10353/**
10354 * Deletes implicit differencing hard disks created either by
10355 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10356 *
10357 * Note that to delete hard disks created by #AttachDevice() this method is
10358 * called from #fixupMedia() when the changes are rolled back.
10359 *
10360 * @note Locks this object and the media tree for writing.
10361 */
10362HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10363{
10364 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10365
10366 AutoCaller autoCaller(this);
10367 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10368
10369 AutoMultiWriteLock2 alock(this->lockHandle(),
10370 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10371
10372 /* We absolutely must have backed up state. */
10373 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10374
10375 HRESULT rc = S_OK;
10376 MachineState_T oldState = mData->mMachineState;
10377
10378 /* will release the lock before the potentially lengthy operation,
10379 * so protect with the special state (unless already protected) */
10380 if ( oldState != MachineState_Saving
10381 && oldState != MachineState_LiveSnapshotting
10382 && oldState != MachineState_RestoringSnapshot
10383 && oldState != MachineState_DeletingSnapshot
10384 && oldState != MachineState_DeletingSnapshotOnline
10385 && oldState != MachineState_DeletingSnapshotPaused
10386 )
10387 setMachineState(MachineState_SettingUp);
10388
10389 // use appropriate locked media map (online or offline)
10390 MediumLockListMap lockedMediaOffline;
10391 MediumLockListMap *lockedMediaMap;
10392 if (aOnline)
10393 lockedMediaMap = &mData->mSession.mLockedMedia;
10394 else
10395 lockedMediaMap = &lockedMediaOffline;
10396
10397 try
10398 {
10399 if (!aOnline)
10400 {
10401 /* lock all attached hard disks early to detect "in use"
10402 * situations before deleting actual diffs */
10403 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10404 it != mMediaData->mAttachments.end();
10405 ++it)
10406 {
10407 MediumAttachment* pAtt = *it;
10408 if (pAtt->getType() == DeviceType_HardDisk)
10409 {
10410 Medium* pMedium = pAtt->getMedium();
10411 Assert(pMedium);
10412
10413 MediumLockList *pMediumLockList(new MediumLockList());
10414 alock.release();
10415 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10416 false /* fMediumLockWrite */,
10417 NULL,
10418 *pMediumLockList);
10419 alock.acquire();
10420
10421 if (FAILED(rc))
10422 {
10423 delete pMediumLockList;
10424 throw rc;
10425 }
10426
10427 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10428 if (FAILED(rc))
10429 throw rc;
10430 }
10431 }
10432
10433 if (FAILED(rc))
10434 throw rc;
10435 } // end of offline
10436
10437 /* Lock lists are now up to date and include implicitly created media */
10438
10439 /* Go through remembered attachments and delete all implicitly created
10440 * diffs and fix up the attachment information */
10441 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10442 MediaData::AttachmentList implicitAtts;
10443 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10444 it != mMediaData->mAttachments.end();
10445 ++it)
10446 {
10447 ComObjPtr<MediumAttachment> pAtt = *it;
10448 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10449 if (pMedium.isNull())
10450 continue;
10451
10452 // Implicit attachments go on the list for deletion and back references are removed.
10453 if (pAtt->isImplicit())
10454 {
10455 /* Deassociate and mark for deletion */
10456 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10457 rc = pMedium->removeBackReference(mData->mUuid);
10458 if (FAILED(rc))
10459 throw rc;
10460 implicitAtts.push_back(pAtt);
10461 continue;
10462 }
10463
10464 /* Was this medium attached before? */
10465 if (!findAttachment(oldAtts, pMedium))
10466 {
10467 /* no: de-associate */
10468 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10469 rc = pMedium->removeBackReference(mData->mUuid);
10470 if (FAILED(rc))
10471 throw rc;
10472 continue;
10473 }
10474 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10475 }
10476
10477 /* If there are implicit attachments to delete, throw away the lock
10478 * map contents (which will unlock all media) since the medium
10479 * attachments will be rolled back. Below we need to completely
10480 * recreate the lock map anyway since it is infinitely complex to
10481 * do this incrementally (would need reconstructing each attachment
10482 * change, which would be extremely hairy). */
10483 if (implicitAtts.size() != 0)
10484 {
10485 ErrorInfoKeeper eik;
10486
10487 HRESULT rc1 = lockedMediaMap->Clear();
10488 AssertComRC(rc1);
10489 }
10490
10491 /* rollback hard disk changes */
10492 mMediaData.rollback();
10493
10494 MultiResult mrc(S_OK);
10495
10496 // Delete unused implicit diffs.
10497 if (implicitAtts.size() != 0)
10498 {
10499 alock.release();
10500
10501 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10502 it != implicitAtts.end();
10503 ++it)
10504 {
10505 // Remove medium associated with this attachment.
10506 ComObjPtr<MediumAttachment> pAtt = *it;
10507 Assert(pAtt);
10508 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10509 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10510 Assert(pMedium);
10511
10512 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10513 // continue on delete failure, just collect error messages
10514 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10515 mrc = rc;
10516 }
10517
10518 alock.acquire();
10519
10520 /* if there is a VM recreate media lock map as mentioned above,
10521 * otherwise it is a waste of time and we leave things unlocked */
10522 if (aOnline)
10523 {
10524 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10525 /* must never be NULL, but better safe than sorry */
10526 if (!pMachine.isNull())
10527 {
10528 alock.release();
10529 rc = mData->mSession.mMachine->lockMedia();
10530 alock.acquire();
10531 if (FAILED(rc))
10532 throw rc;
10533 }
10534 }
10535 }
10536 }
10537 catch (HRESULT aRC) {rc = aRC;}
10538
10539 if (mData->mMachineState == MachineState_SettingUp)
10540 setMachineState(oldState);
10541
10542 /* unlock all hard disks we locked when there is no VM */
10543 if (!aOnline)
10544 {
10545 ErrorInfoKeeper eik;
10546
10547 HRESULT rc1 = lockedMediaMap->Clear();
10548 AssertComRC(rc1);
10549 }
10550
10551 return rc;
10552}
10553
10554
10555/**
10556 * Looks through the given list of media attachments for one with the given parameters
10557 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10558 * can be searched as well if needed.
10559 *
10560 * @param list
10561 * @param aControllerName
10562 * @param aControllerPort
10563 * @param aDevice
10564 * @return
10565 */
10566MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10567 IN_BSTR aControllerName,
10568 LONG aControllerPort,
10569 LONG aDevice)
10570{
10571 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10572 it != ll.end();
10573 ++it)
10574 {
10575 MediumAttachment *pAttach = *it;
10576 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10577 return pAttach;
10578 }
10579
10580 return NULL;
10581}
10582
10583/**
10584 * Looks through the given list of media attachments for one with the given parameters
10585 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10586 * can be searched as well if needed.
10587 *
10588 * @param list
10589 * @param aControllerName
10590 * @param aControllerPort
10591 * @param aDevice
10592 * @return
10593 */
10594MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10595 ComObjPtr<Medium> pMedium)
10596{
10597 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10598 it != ll.end();
10599 ++it)
10600 {
10601 MediumAttachment *pAttach = *it;
10602 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10603 if (pMediumThis == pMedium)
10604 return pAttach;
10605 }
10606
10607 return NULL;
10608}
10609
10610/**
10611 * Looks through the given list of media attachments for one with the given parameters
10612 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10613 * can be searched as well if needed.
10614 *
10615 * @param list
10616 * @param aControllerName
10617 * @param aControllerPort
10618 * @param aDevice
10619 * @return
10620 */
10621MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10622 Guid &id)
10623{
10624 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10625 it != ll.end();
10626 ++it)
10627 {
10628 MediumAttachment *pAttach = *it;
10629 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10630 if (pMediumThis->getId() == id)
10631 return pAttach;
10632 }
10633
10634 return NULL;
10635}
10636
10637/**
10638 * Main implementation for Machine::DetachDevice. This also gets called
10639 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10640 *
10641 * @param pAttach Medium attachment to detach.
10642 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10643 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10644 * @return
10645 */
10646HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10647 AutoWriteLock &writeLock,
10648 Snapshot *pSnapshot)
10649{
10650 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10651 DeviceType_T mediumType = pAttach->getType();
10652
10653 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10654
10655 if (pAttach->isImplicit())
10656 {
10657 /* attempt to implicitly delete the implicitly created diff */
10658
10659 /// @todo move the implicit flag from MediumAttachment to Medium
10660 /// and forbid any hard disk operation when it is implicit. Or maybe
10661 /// a special media state for it to make it even more simple.
10662
10663 Assert(mMediaData.isBackedUp());
10664
10665 /* will release the lock before the potentially lengthy operation, so
10666 * protect with the special state */
10667 MachineState_T oldState = mData->mMachineState;
10668 setMachineState(MachineState_SettingUp);
10669
10670 writeLock.release();
10671
10672 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10673 true /*aWait*/);
10674
10675 writeLock.acquire();
10676
10677 setMachineState(oldState);
10678
10679 if (FAILED(rc)) return rc;
10680 }
10681
10682 setModified(IsModified_Storage);
10683 mMediaData.backup();
10684 mMediaData->mAttachments.remove(pAttach);
10685
10686 if (!oldmedium.isNull())
10687 {
10688 // if this is from a snapshot, do not defer detachment to commitMedia()
10689 if (pSnapshot)
10690 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10691 // else if non-hard disk media, do not defer detachment to commitMedia() either
10692 else if (mediumType != DeviceType_HardDisk)
10693 oldmedium->removeBackReference(mData->mUuid);
10694 }
10695
10696 return S_OK;
10697}
10698
10699/**
10700 * Goes thru all media of the given list and
10701 *
10702 * 1) calls detachDevice() on each of them for this machine and
10703 * 2) adds all Medium objects found in the process to the given list,
10704 * depending on cleanupMode.
10705 *
10706 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10707 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10708 * media to the list.
10709 *
10710 * This gets called from Machine::Unregister, both for the actual Machine and
10711 * the SnapshotMachine objects that might be found in the snapshots.
10712 *
10713 * Requires caller and locking. The machine lock must be passed in because it
10714 * will be passed on to detachDevice which needs it for temporary unlocking.
10715 *
10716 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10717 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10718 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10719 * otherwise no media get added.
10720 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10721 * @return
10722 */
10723HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10724 Snapshot *pSnapshot,
10725 CleanupMode_T cleanupMode,
10726 MediaList &llMedia)
10727{
10728 Assert(isWriteLockOnCurrentThread());
10729
10730 HRESULT rc;
10731
10732 // make a temporary list because detachDevice invalidates iterators into
10733 // mMediaData->mAttachments
10734 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10735
10736 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10737 it != llAttachments2.end();
10738 ++it)
10739 {
10740 ComObjPtr<MediumAttachment> &pAttach = *it;
10741 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10742
10743 if (!pMedium.isNull())
10744 {
10745 AutoCaller mac(pMedium);
10746 if (FAILED(mac.rc())) return mac.rc();
10747 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10748 DeviceType_T devType = pMedium->getDeviceType();
10749 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10750 && devType == DeviceType_HardDisk)
10751 || (cleanupMode == CleanupMode_Full)
10752 )
10753 {
10754 llMedia.push_back(pMedium);
10755 ComObjPtr<Medium> pParent = pMedium->getParent();
10756 /*
10757 * Search for medias which are not attached to any machine, but
10758 * in the chain to an attached disk. Mediums are only consided
10759 * if they are:
10760 * - have only one child
10761 * - no references to any machines
10762 * - are of normal medium type
10763 */
10764 while (!pParent.isNull())
10765 {
10766 AutoCaller mac1(pParent);
10767 if (FAILED(mac1.rc())) return mac1.rc();
10768 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10769 if (pParent->getChildren().size() == 1)
10770 {
10771 if ( pParent->getMachineBackRefCount() == 0
10772 && pParent->getType() == MediumType_Normal
10773 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10774 llMedia.push_back(pParent);
10775 }
10776 else
10777 break;
10778 pParent = pParent->getParent();
10779 }
10780 }
10781 }
10782
10783 // real machine: then we need to use the proper method
10784 rc = detachDevice(pAttach, writeLock, pSnapshot);
10785
10786 if (FAILED(rc))
10787 return rc;
10788 }
10789
10790 return S_OK;
10791}
10792
10793/**
10794 * Perform deferred hard disk detachments.
10795 *
10796 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10797 * backed up).
10798 *
10799 * If @a aOnline is @c true then this method will also unlock the old hard disks
10800 * for which the new implicit diffs were created and will lock these new diffs for
10801 * writing.
10802 *
10803 * @param aOnline Whether the VM was online prior to this operation.
10804 *
10805 * @note Locks this object for writing!
10806 */
10807void Machine::commitMedia(bool aOnline /*= false*/)
10808{
10809 AutoCaller autoCaller(this);
10810 AssertComRCReturnVoid(autoCaller.rc());
10811
10812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10813
10814 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10815
10816 HRESULT rc = S_OK;
10817
10818 /* no attach/detach operations -- nothing to do */
10819 if (!mMediaData.isBackedUp())
10820 return;
10821
10822 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10823 bool fMediaNeedsLocking = false;
10824
10825 /* enumerate new attachments */
10826 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10827 it != mMediaData->mAttachments.end();
10828 ++it)
10829 {
10830 MediumAttachment *pAttach = *it;
10831
10832 pAttach->commit();
10833
10834 Medium* pMedium = pAttach->getMedium();
10835 bool fImplicit = pAttach->isImplicit();
10836
10837 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10838 (pMedium) ? pMedium->getName().c_str() : "NULL",
10839 fImplicit));
10840
10841 /** @todo convert all this Machine-based voodoo to MediumAttachment
10842 * based commit logic. */
10843 if (fImplicit)
10844 {
10845 /* convert implicit attachment to normal */
10846 pAttach->setImplicit(false);
10847
10848 if ( aOnline
10849 && pMedium
10850 && pAttach->getType() == DeviceType_HardDisk
10851 )
10852 {
10853 ComObjPtr<Medium> parent = pMedium->getParent();
10854 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10855
10856 /* update the appropriate lock list */
10857 MediumLockList *pMediumLockList;
10858 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10859 AssertComRC(rc);
10860 if (pMediumLockList)
10861 {
10862 /* unlock if there's a need to change the locking */
10863 if (!fMediaNeedsLocking)
10864 {
10865 rc = mData->mSession.mLockedMedia.Unlock();
10866 AssertComRC(rc);
10867 fMediaNeedsLocking = true;
10868 }
10869 rc = pMediumLockList->Update(parent, false);
10870 AssertComRC(rc);
10871 rc = pMediumLockList->Append(pMedium, true);
10872 AssertComRC(rc);
10873 }
10874 }
10875
10876 continue;
10877 }
10878
10879 if (pMedium)
10880 {
10881 /* was this medium attached before? */
10882 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10883 oldIt != oldAtts.end();
10884 ++oldIt)
10885 {
10886 MediumAttachment *pOldAttach = *oldIt;
10887 if (pOldAttach->getMedium() == pMedium)
10888 {
10889 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10890
10891 /* yes: remove from old to avoid de-association */
10892 oldAtts.erase(oldIt);
10893 break;
10894 }
10895 }
10896 }
10897 }
10898
10899 /* enumerate remaining old attachments and de-associate from the
10900 * current machine state */
10901 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10902 it != oldAtts.end();
10903 ++it)
10904 {
10905 MediumAttachment *pAttach = *it;
10906 Medium* pMedium = pAttach->getMedium();
10907
10908 /* Detach only hard disks, since DVD/floppy media is detached
10909 * instantly in MountMedium. */
10910 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10911 {
10912 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10913
10914 /* now de-associate from the current machine state */
10915 rc = pMedium->removeBackReference(mData->mUuid);
10916 AssertComRC(rc);
10917
10918 if (aOnline)
10919 {
10920 /* unlock since medium is not used anymore */
10921 MediumLockList *pMediumLockList;
10922 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10923 AssertComRC(rc);
10924 if (pMediumLockList)
10925 {
10926 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10927 AssertComRC(rc);
10928 }
10929 }
10930 }
10931 }
10932
10933 /* take media locks again so that the locking state is consistent */
10934 if (fMediaNeedsLocking)
10935 {
10936 Assert(aOnline);
10937 rc = mData->mSession.mLockedMedia.Lock();
10938 AssertComRC(rc);
10939 }
10940
10941 /* commit the hard disk changes */
10942 mMediaData.commit();
10943
10944 if (isSessionMachine())
10945 {
10946 /*
10947 * Update the parent machine to point to the new owner.
10948 * This is necessary because the stored parent will point to the
10949 * session machine otherwise and cause crashes or errors later
10950 * when the session machine gets invalid.
10951 */
10952 /** @todo Change the MediumAttachment class to behave like any other
10953 * class in this regard by creating peer MediumAttachment
10954 * objects for session machines and share the data with the peer
10955 * machine.
10956 */
10957 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10958 it != mMediaData->mAttachments.end();
10959 ++it)
10960 {
10961 (*it)->updateParentMachine(mPeer);
10962 }
10963
10964 /* attach new data to the primary machine and reshare it */
10965 mPeer->mMediaData.attach(mMediaData);
10966 }
10967
10968 return;
10969}
10970
10971/**
10972 * Perform deferred deletion of implicitly created diffs.
10973 *
10974 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10975 * backed up).
10976 *
10977 * @note Locks this object for writing!
10978 */
10979void Machine::rollbackMedia()
10980{
10981 AutoCaller autoCaller(this);
10982 AssertComRCReturnVoid(autoCaller.rc());
10983
10984 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10985 LogFlowThisFunc(("Entering rollbackMedia\n"));
10986
10987 HRESULT rc = S_OK;
10988
10989 /* no attach/detach operations -- nothing to do */
10990 if (!mMediaData.isBackedUp())
10991 return;
10992
10993 /* enumerate new attachments */
10994 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10995 it != mMediaData->mAttachments.end();
10996 ++it)
10997 {
10998 MediumAttachment *pAttach = *it;
10999 /* Fix up the backrefs for DVD/floppy media. */
11000 if (pAttach->getType() != DeviceType_HardDisk)
11001 {
11002 Medium* pMedium = pAttach->getMedium();
11003 if (pMedium)
11004 {
11005 rc = pMedium->removeBackReference(mData->mUuid);
11006 AssertComRC(rc);
11007 }
11008 }
11009
11010 (*it)->rollback();
11011
11012 pAttach = *it;
11013 /* Fix up the backrefs for DVD/floppy media. */
11014 if (pAttach->getType() != DeviceType_HardDisk)
11015 {
11016 Medium* pMedium = pAttach->getMedium();
11017 if (pMedium)
11018 {
11019 rc = pMedium->addBackReference(mData->mUuid);
11020 AssertComRC(rc);
11021 }
11022 }
11023 }
11024
11025 /** @todo convert all this Machine-based voodoo to MediumAttachment
11026 * based rollback logic. */
11027 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11028
11029 return;
11030}
11031
11032/**
11033 * Returns true if the settings file is located in the directory named exactly
11034 * as the machine; this means, among other things, that the machine directory
11035 * should be auto-renamed.
11036 *
11037 * @param aSettingsDir if not NULL, the full machine settings file directory
11038 * name will be assigned there.
11039 *
11040 * @note Doesn't lock anything.
11041 * @note Not thread safe (must be called from this object's lock).
11042 */
11043bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11044{
11045 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11046 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11047 if (aSettingsDir)
11048 *aSettingsDir = strMachineDirName;
11049 strMachineDirName.stripPath(); // vmname
11050 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11051 strConfigFileOnly.stripPath() // vmname.vbox
11052 .stripExt(); // vmname
11053 /** @todo hack, make somehow use of ComposeMachineFilename */
11054 if (mUserData->s.fDirectoryIncludesUUID)
11055 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11056
11057 AssertReturn(!strMachineDirName.isEmpty(), false);
11058 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11059
11060 return strMachineDirName == strConfigFileOnly;
11061}
11062
11063/**
11064 * Discards all changes to machine settings.
11065 *
11066 * @param aNotify Whether to notify the direct session about changes or not.
11067 *
11068 * @note Locks objects for writing!
11069 */
11070void Machine::rollback(bool aNotify)
11071{
11072 AutoCaller autoCaller(this);
11073 AssertComRCReturn(autoCaller.rc(), (void)0);
11074
11075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11076
11077 if (!mStorageControllers.isNull())
11078 {
11079 if (mStorageControllers.isBackedUp())
11080 {
11081 /* unitialize all new devices (absent in the backed up list). */
11082 StorageControllerList::const_iterator it = mStorageControllers->begin();
11083 StorageControllerList *backedList = mStorageControllers.backedUpData();
11084 while (it != mStorageControllers->end())
11085 {
11086 if ( std::find(backedList->begin(), backedList->end(), *it)
11087 == backedList->end()
11088 )
11089 {
11090 (*it)->uninit();
11091 }
11092 ++it;
11093 }
11094
11095 /* restore the list */
11096 mStorageControllers.rollback();
11097 }
11098
11099 /* rollback any changes to devices after restoring the list */
11100 if (mData->flModifications & IsModified_Storage)
11101 {
11102 StorageControllerList::const_iterator it = mStorageControllers->begin();
11103 while (it != mStorageControllers->end())
11104 {
11105 (*it)->rollback();
11106 ++it;
11107 }
11108 }
11109 }
11110
11111 mUserData.rollback();
11112
11113 mHWData.rollback();
11114
11115 if (mData->flModifications & IsModified_Storage)
11116 rollbackMedia();
11117
11118 if (mBIOSSettings)
11119 mBIOSSettings->rollback();
11120
11121 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11122 mVRDEServer->rollback();
11123
11124 if (mAudioAdapter)
11125 mAudioAdapter->rollback();
11126
11127 if (mUSBController && (mData->flModifications & IsModified_USB))
11128 mUSBController->rollback();
11129
11130 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11131 mBandwidthControl->rollback();
11132
11133 if (!mHWData.isNull())
11134 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11135 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11136 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11137 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11138
11139 if (mData->flModifications & IsModified_NetworkAdapters)
11140 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11141 if ( mNetworkAdapters[slot]
11142 && mNetworkAdapters[slot]->isModified())
11143 {
11144 mNetworkAdapters[slot]->rollback();
11145 networkAdapters[slot] = mNetworkAdapters[slot];
11146 }
11147
11148 if (mData->flModifications & IsModified_SerialPorts)
11149 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11150 if ( mSerialPorts[slot]
11151 && mSerialPorts[slot]->isModified())
11152 {
11153 mSerialPorts[slot]->rollback();
11154 serialPorts[slot] = mSerialPorts[slot];
11155 }
11156
11157 if (mData->flModifications & IsModified_ParallelPorts)
11158 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11159 if ( mParallelPorts[slot]
11160 && mParallelPorts[slot]->isModified())
11161 {
11162 mParallelPorts[slot]->rollback();
11163 parallelPorts[slot] = mParallelPorts[slot];
11164 }
11165
11166 if (aNotify)
11167 {
11168 /* inform the direct session about changes */
11169
11170 ComObjPtr<Machine> that = this;
11171 uint32_t flModifications = mData->flModifications;
11172 alock.release();
11173
11174 if (flModifications & IsModified_SharedFolders)
11175 that->onSharedFolderChange();
11176
11177 if (flModifications & IsModified_VRDEServer)
11178 that->onVRDEServerChange(/* aRestart */ TRUE);
11179 if (flModifications & IsModified_USB)
11180 that->onUSBControllerChange();
11181
11182 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11183 if (networkAdapters[slot])
11184 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11185 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11186 if (serialPorts[slot])
11187 that->onSerialPortChange(serialPorts[slot]);
11188 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11189 if (parallelPorts[slot])
11190 that->onParallelPortChange(parallelPorts[slot]);
11191
11192 if (flModifications & IsModified_Storage)
11193 that->onStorageControllerChange();
11194
11195#if 0
11196 if (flModifications & IsModified_BandwidthControl)
11197 that->onBandwidthControlChange();
11198#endif
11199 }
11200}
11201
11202/**
11203 * Commits all the changes to machine settings.
11204 *
11205 * Note that this operation is supposed to never fail.
11206 *
11207 * @note Locks this object and children for writing.
11208 */
11209void Machine::commit()
11210{
11211 AutoCaller autoCaller(this);
11212 AssertComRCReturnVoid(autoCaller.rc());
11213
11214 AutoCaller peerCaller(mPeer);
11215 AssertComRCReturnVoid(peerCaller.rc());
11216
11217 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11218
11219 /*
11220 * use safe commit to ensure Snapshot machines (that share mUserData)
11221 * will still refer to a valid memory location
11222 */
11223 mUserData.commitCopy();
11224
11225 mHWData.commit();
11226
11227 if (mMediaData.isBackedUp())
11228 commitMedia();
11229
11230 mBIOSSettings->commit();
11231 mVRDEServer->commit();
11232 mAudioAdapter->commit();
11233 mUSBController->commit();
11234 mBandwidthControl->commit();
11235
11236 /* Since mNetworkAdapters is a list which might have been changed (resized)
11237 * without using the Backupable<> template we need to handle the copying
11238 * of the list entries manually, including the creation of peers for the
11239 * new objects. */
11240 bool commitNetworkAdapters = false;
11241 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11242 if (mPeer)
11243 {
11244 /* commit everything, even the ones which will go away */
11245 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11246 mNetworkAdapters[slot]->commit();
11247 /* copy over the new entries, creating a peer and uninit the original */
11248 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11249 for (size_t slot = 0; slot < newSize; slot++)
11250 {
11251 /* look if this adapter has a peer device */
11252 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11253 if (!peer)
11254 {
11255 /* no peer means the adapter is a newly created one;
11256 * create a peer owning data this data share it with */
11257 peer.createObject();
11258 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11259 }
11260 mPeer->mNetworkAdapters[slot] = peer;
11261 }
11262 /* uninit any no longer needed network adapters */
11263 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11264 mNetworkAdapters[slot]->uninit();
11265 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11266 {
11267 if (mPeer->mNetworkAdapters[slot])
11268 mPeer->mNetworkAdapters[slot]->uninit();
11269 }
11270 /* Keep the original network adapter count until this point, so that
11271 * discarding a chipset type change will not lose settings. */
11272 mNetworkAdapters.resize(newSize);
11273 mPeer->mNetworkAdapters.resize(newSize);
11274 }
11275 else
11276 {
11277 /* we have no peer (our parent is the newly created machine);
11278 * just commit changes to the network adapters */
11279 commitNetworkAdapters = true;
11280 }
11281 if (commitNetworkAdapters)
11282 {
11283 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11284 mNetworkAdapters[slot]->commit();
11285 }
11286
11287 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11288 mSerialPorts[slot]->commit();
11289 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11290 mParallelPorts[slot]->commit();
11291
11292 bool commitStorageControllers = false;
11293
11294 if (mStorageControllers.isBackedUp())
11295 {
11296 mStorageControllers.commit();
11297
11298 if (mPeer)
11299 {
11300 /* Commit all changes to new controllers (this will reshare data with
11301 * peers for those who have peers) */
11302 StorageControllerList *newList = new StorageControllerList();
11303 StorageControllerList::const_iterator it = mStorageControllers->begin();
11304 while (it != mStorageControllers->end())
11305 {
11306 (*it)->commit();
11307
11308 /* look if this controller has a peer device */
11309 ComObjPtr<StorageController> peer = (*it)->getPeer();
11310 if (!peer)
11311 {
11312 /* no peer means the device is a newly created one;
11313 * create a peer owning data this device share it with */
11314 peer.createObject();
11315 peer->init(mPeer, *it, true /* aReshare */);
11316 }
11317 else
11318 {
11319 /* remove peer from the old list */
11320 mPeer->mStorageControllers->remove(peer);
11321 }
11322 /* and add it to the new list */
11323 newList->push_back(peer);
11324
11325 ++it;
11326 }
11327
11328 /* uninit old peer's controllers that are left */
11329 it = mPeer->mStorageControllers->begin();
11330 while (it != mPeer->mStorageControllers->end())
11331 {
11332 (*it)->uninit();
11333 ++it;
11334 }
11335
11336 /* attach new list of controllers to our peer */
11337 mPeer->mStorageControllers.attach(newList);
11338 }
11339 else
11340 {
11341 /* we have no peer (our parent is the newly created machine);
11342 * just commit changes to devices */
11343 commitStorageControllers = true;
11344 }
11345 }
11346 else
11347 {
11348 /* the list of controllers itself is not changed,
11349 * just commit changes to controllers themselves */
11350 commitStorageControllers = true;
11351 }
11352
11353 if (commitStorageControllers)
11354 {
11355 StorageControllerList::const_iterator it = mStorageControllers->begin();
11356 while (it != mStorageControllers->end())
11357 {
11358 (*it)->commit();
11359 ++it;
11360 }
11361 }
11362
11363 if (isSessionMachine())
11364 {
11365 /* attach new data to the primary machine and reshare it */
11366 mPeer->mUserData.attach(mUserData);
11367 mPeer->mHWData.attach(mHWData);
11368 /* mMediaData is reshared by fixupMedia */
11369 // mPeer->mMediaData.attach(mMediaData);
11370 Assert(mPeer->mMediaData.data() == mMediaData.data());
11371 }
11372}
11373
11374/**
11375 * Copies all the hardware data from the given machine.
11376 *
11377 * Currently, only called when the VM is being restored from a snapshot. In
11378 * particular, this implies that the VM is not running during this method's
11379 * call.
11380 *
11381 * @note This method must be called from under this object's lock.
11382 *
11383 * @note This method doesn't call #commit(), so all data remains backed up and
11384 * unsaved.
11385 */
11386void Machine::copyFrom(Machine *aThat)
11387{
11388 AssertReturnVoid(!isSnapshotMachine());
11389 AssertReturnVoid(aThat->isSnapshotMachine());
11390
11391 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11392
11393 mHWData.assignCopy(aThat->mHWData);
11394
11395 // create copies of all shared folders (mHWData after attaching a copy
11396 // contains just references to original objects)
11397 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11398 it != mHWData->mSharedFolders.end();
11399 ++it)
11400 {
11401 ComObjPtr<SharedFolder> folder;
11402 folder.createObject();
11403 HRESULT rc = folder->initCopy(getMachine(), *it);
11404 AssertComRC(rc);
11405 *it = folder;
11406 }
11407
11408 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11409 mVRDEServer->copyFrom(aThat->mVRDEServer);
11410 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11411 mUSBController->copyFrom(aThat->mUSBController);
11412 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11413
11414 /* create private copies of all controllers */
11415 mStorageControllers.backup();
11416 mStorageControllers->clear();
11417 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11418 it != aThat->mStorageControllers->end();
11419 ++it)
11420 {
11421 ComObjPtr<StorageController> ctrl;
11422 ctrl.createObject();
11423 ctrl->initCopy(this, *it);
11424 mStorageControllers->push_back(ctrl);
11425 }
11426
11427 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11428 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11429 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11430 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11431 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11432 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11433 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11434}
11435
11436/**
11437 * Returns whether the given storage controller is hotplug capable.
11438 *
11439 * @returns true if the controller supports hotplugging
11440 * false otherwise.
11441 * @param enmCtrlType The controller type to check for.
11442 */
11443bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11444{
11445 switch (enmCtrlType)
11446 {
11447 case StorageControllerType_IntelAhci:
11448 return true;
11449 case StorageControllerType_LsiLogic:
11450 case StorageControllerType_LsiLogicSas:
11451 case StorageControllerType_BusLogic:
11452 case StorageControllerType_PIIX3:
11453 case StorageControllerType_PIIX4:
11454 case StorageControllerType_ICH6:
11455 case StorageControllerType_I82078:
11456 default:
11457 return false;
11458 }
11459}
11460
11461#ifdef VBOX_WITH_RESOURCE_USAGE_API
11462
11463void Machine::getDiskList(MediaList &list)
11464{
11465 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11466 it != mMediaData->mAttachments.end();
11467 ++it)
11468 {
11469 MediumAttachment* pAttach = *it;
11470 /* just in case */
11471 AssertStmt(pAttach, continue);
11472
11473 AutoCaller localAutoCallerA(pAttach);
11474 if (FAILED(localAutoCallerA.rc())) continue;
11475
11476 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11477
11478 if (pAttach->getType() == DeviceType_HardDisk)
11479 list.push_back(pAttach->getMedium());
11480 }
11481}
11482
11483void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11484{
11485 AssertReturnVoid(isWriteLockOnCurrentThread());
11486 AssertPtrReturnVoid(aCollector);
11487
11488 pm::CollectorHAL *hal = aCollector->getHAL();
11489 /* Create sub metrics */
11490 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11491 "Percentage of processor time spent in user mode by the VM process.");
11492 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11493 "Percentage of processor time spent in kernel mode by the VM process.");
11494 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11495 "Size of resident portion of VM process in memory.");
11496 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11497 "Actual size of all VM disks combined.");
11498 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11499 "Network receive rate.");
11500 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11501 "Network transmit rate.");
11502 /* Create and register base metrics */
11503 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11504 cpuLoadUser, cpuLoadKernel);
11505 aCollector->registerBaseMetric(cpuLoad);
11506 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11507 ramUsageUsed);
11508 aCollector->registerBaseMetric(ramUsage);
11509 MediaList disks;
11510 getDiskList(disks);
11511 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11512 diskUsageUsed);
11513 aCollector->registerBaseMetric(diskUsage);
11514
11515 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11516 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11517 new pm::AggregateAvg()));
11518 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11519 new pm::AggregateMin()));
11520 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11521 new pm::AggregateMax()));
11522 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11523 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11524 new pm::AggregateAvg()));
11525 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11526 new pm::AggregateMin()));
11527 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11528 new pm::AggregateMax()));
11529
11530 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11531 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11532 new pm::AggregateAvg()));
11533 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11534 new pm::AggregateMin()));
11535 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11536 new pm::AggregateMax()));
11537
11538 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11539 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11540 new pm::AggregateAvg()));
11541 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11542 new pm::AggregateMin()));
11543 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11544 new pm::AggregateMax()));
11545
11546
11547 /* Guest metrics collector */
11548 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11549 aCollector->registerGuest(mCollectorGuest);
11550 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11551 this, __PRETTY_FUNCTION__, mCollectorGuest));
11552
11553 /* Create sub metrics */
11554 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11555 "Percentage of processor time spent in user mode as seen by the guest.");
11556 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11557 "Percentage of processor time spent in kernel mode as seen by the guest.");
11558 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11559 "Percentage of processor time spent idling as seen by the guest.");
11560
11561 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11562 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11563 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11564 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11565 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11566 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11567
11568 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11569
11570 /* Create and register base metrics */
11571 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11572 machineNetRx, machineNetTx);
11573 aCollector->registerBaseMetric(machineNetRate);
11574
11575 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11576 guestLoadUser, guestLoadKernel, guestLoadIdle);
11577 aCollector->registerBaseMetric(guestCpuLoad);
11578
11579 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11580 guestMemTotal, guestMemFree,
11581 guestMemBalloon, guestMemShared,
11582 guestMemCache, guestPagedTotal);
11583 aCollector->registerBaseMetric(guestCpuMem);
11584
11585 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11586 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11587 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11588 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11589
11590 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11591 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11592 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11593 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11594
11595 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11596 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11597 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11598 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11599
11600 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11601 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11602 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11603 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11604
11605 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11606 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11607 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11608 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11609
11610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11614
11615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11619
11620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11623 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11624
11625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11626 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11627 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11628 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11629
11630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11633 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11634
11635 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11636 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11637 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11638 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11639}
11640
11641void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11642{
11643 AssertReturnVoid(isWriteLockOnCurrentThread());
11644
11645 if (aCollector)
11646 {
11647 aCollector->unregisterMetricsFor(aMachine);
11648 aCollector->unregisterBaseMetricsFor(aMachine);
11649 }
11650}
11651
11652#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11653
11654
11655////////////////////////////////////////////////////////////////////////////////
11656
11657DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11658
11659HRESULT SessionMachine::FinalConstruct()
11660{
11661 LogFlowThisFunc(("\n"));
11662
11663#if defined(RT_OS_WINDOWS)
11664 mIPCSem = NULL;
11665#elif defined(RT_OS_OS2)
11666 mIPCSem = NULLHANDLE;
11667#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11668 mIPCSem = -1;
11669#else
11670# error "Port me!"
11671#endif
11672
11673 return BaseFinalConstruct();
11674}
11675
11676void SessionMachine::FinalRelease()
11677{
11678 LogFlowThisFunc(("\n"));
11679
11680 uninit(Uninit::Unexpected);
11681
11682 BaseFinalRelease();
11683}
11684
11685/**
11686 * @note Must be called only by Machine::openSession() from its own write lock.
11687 */
11688HRESULT SessionMachine::init(Machine *aMachine)
11689{
11690 LogFlowThisFuncEnter();
11691 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11692
11693 AssertReturn(aMachine, E_INVALIDARG);
11694
11695 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11696
11697 /* Enclose the state transition NotReady->InInit->Ready */
11698 AutoInitSpan autoInitSpan(this);
11699 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11700
11701 /* create the interprocess semaphore */
11702#if defined(RT_OS_WINDOWS)
11703 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11704 for (size_t i = 0; i < mIPCSemName.length(); i++)
11705 if (mIPCSemName.raw()[i] == '\\')
11706 mIPCSemName.raw()[i] = '/';
11707 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11708 ComAssertMsgRet(mIPCSem,
11709 ("Cannot create IPC mutex '%ls', err=%d",
11710 mIPCSemName.raw(), ::GetLastError()),
11711 E_FAIL);
11712#elif defined(RT_OS_OS2)
11713 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11714 aMachine->mData->mUuid.raw());
11715 mIPCSemName = ipcSem;
11716 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11717 ComAssertMsgRet(arc == NO_ERROR,
11718 ("Cannot create IPC mutex '%s', arc=%ld",
11719 ipcSem.c_str(), arc),
11720 E_FAIL);
11721#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11722# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11723# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11724 /** @todo Check that this still works correctly. */
11725 AssertCompileSize(key_t, 8);
11726# else
11727 AssertCompileSize(key_t, 4);
11728# endif
11729 key_t key;
11730 mIPCSem = -1;
11731 mIPCKey = "0";
11732 for (uint32_t i = 0; i < 1 << 24; i++)
11733 {
11734 key = ((uint32_t)'V' << 24) | i;
11735 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11736 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11737 {
11738 mIPCSem = sem;
11739 if (sem >= 0)
11740 mIPCKey = BstrFmt("%u", key);
11741 break;
11742 }
11743 }
11744# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11745 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11746 char *pszSemName = NULL;
11747 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11748 key_t key = ::ftok(pszSemName, 'V');
11749 RTStrFree(pszSemName);
11750
11751 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11752# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11753
11754 int errnoSave = errno;
11755 if (mIPCSem < 0 && errnoSave == ENOSYS)
11756 {
11757 setError(E_FAIL,
11758 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11759 "support for SysV IPC. Check the host kernel configuration for "
11760 "CONFIG_SYSVIPC=y"));
11761 return E_FAIL;
11762 }
11763 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11764 * the IPC semaphores */
11765 if (mIPCSem < 0 && errnoSave == ENOSPC)
11766 {
11767#ifdef RT_OS_LINUX
11768 setError(E_FAIL,
11769 tr("Cannot create IPC semaphore because the system limit for the "
11770 "maximum number of semaphore sets (SEMMNI), or the system wide "
11771 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11772 "current set of SysV IPC semaphores can be determined from "
11773 "the file /proc/sysvipc/sem"));
11774#else
11775 setError(E_FAIL,
11776 tr("Cannot create IPC semaphore because the system-imposed limit "
11777 "on the maximum number of allowed semaphores or semaphore "
11778 "identifiers system-wide would be exceeded"));
11779#endif
11780 return E_FAIL;
11781 }
11782 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11783 E_FAIL);
11784 /* set the initial value to 1 */
11785 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11786 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11787 E_FAIL);
11788#else
11789# error "Port me!"
11790#endif
11791
11792 /* memorize the peer Machine */
11793 unconst(mPeer) = aMachine;
11794 /* share the parent pointer */
11795 unconst(mParent) = aMachine->mParent;
11796
11797 /* take the pointers to data to share */
11798 mData.share(aMachine->mData);
11799 mSSData.share(aMachine->mSSData);
11800
11801 mUserData.share(aMachine->mUserData);
11802 mHWData.share(aMachine->mHWData);
11803 mMediaData.share(aMachine->mMediaData);
11804
11805 mStorageControllers.allocate();
11806 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11807 it != aMachine->mStorageControllers->end();
11808 ++it)
11809 {
11810 ComObjPtr<StorageController> ctl;
11811 ctl.createObject();
11812 ctl->init(this, *it);
11813 mStorageControllers->push_back(ctl);
11814 }
11815
11816 unconst(mBIOSSettings).createObject();
11817 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11818 /* create another VRDEServer object that will be mutable */
11819 unconst(mVRDEServer).createObject();
11820 mVRDEServer->init(this, aMachine->mVRDEServer);
11821 /* create another audio adapter object that will be mutable */
11822 unconst(mAudioAdapter).createObject();
11823 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11824 /* create a list of serial ports that will be mutable */
11825 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11826 {
11827 unconst(mSerialPorts[slot]).createObject();
11828 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11829 }
11830 /* create a list of parallel ports that will be mutable */
11831 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11832 {
11833 unconst(mParallelPorts[slot]).createObject();
11834 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11835 }
11836 /* create another USB controller object that will be mutable */
11837 unconst(mUSBController).createObject();
11838 mUSBController->init(this, aMachine->mUSBController);
11839
11840 /* create a list of network adapters that will be mutable */
11841 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11842 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11843 {
11844 unconst(mNetworkAdapters[slot]).createObject();
11845 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11846 }
11847
11848 /* create another bandwidth control object that will be mutable */
11849 unconst(mBandwidthControl).createObject();
11850 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11851
11852 /* default is to delete saved state on Saved -> PoweredOff transition */
11853 mRemoveSavedState = true;
11854
11855 /* Confirm a successful initialization when it's the case */
11856 autoInitSpan.setSucceeded();
11857
11858 LogFlowThisFuncLeave();
11859 return S_OK;
11860}
11861
11862/**
11863 * Uninitializes this session object. If the reason is other than
11864 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11865 *
11866 * @param aReason uninitialization reason
11867 *
11868 * @note Locks mParent + this object for writing.
11869 */
11870void SessionMachine::uninit(Uninit::Reason aReason)
11871{
11872 LogFlowThisFuncEnter();
11873 LogFlowThisFunc(("reason=%d\n", aReason));
11874
11875 /*
11876 * Strongly reference ourselves to prevent this object deletion after
11877 * mData->mSession.mMachine.setNull() below (which can release the last
11878 * reference and call the destructor). Important: this must be done before
11879 * accessing any members (and before AutoUninitSpan that does it as well).
11880 * This self reference will be released as the very last step on return.
11881 */
11882 ComObjPtr<SessionMachine> selfRef = this;
11883
11884 /* Enclose the state transition Ready->InUninit->NotReady */
11885 AutoUninitSpan autoUninitSpan(this);
11886 if (autoUninitSpan.uninitDone())
11887 {
11888 LogFlowThisFunc(("Already uninitialized\n"));
11889 LogFlowThisFuncLeave();
11890 return;
11891 }
11892
11893 if (autoUninitSpan.initFailed())
11894 {
11895 /* We've been called by init() because it's failed. It's not really
11896 * necessary (nor it's safe) to perform the regular uninit sequence
11897 * below, the following is enough.
11898 */
11899 LogFlowThisFunc(("Initialization failed.\n"));
11900#if defined(RT_OS_WINDOWS)
11901 if (mIPCSem)
11902 ::CloseHandle(mIPCSem);
11903 mIPCSem = NULL;
11904#elif defined(RT_OS_OS2)
11905 if (mIPCSem != NULLHANDLE)
11906 ::DosCloseMutexSem(mIPCSem);
11907 mIPCSem = NULLHANDLE;
11908#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11909 if (mIPCSem >= 0)
11910 ::semctl(mIPCSem, 0, IPC_RMID);
11911 mIPCSem = -1;
11912# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11913 mIPCKey = "0";
11914# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11915#else
11916# error "Port me!"
11917#endif
11918 uninitDataAndChildObjects();
11919 mData.free();
11920 unconst(mParent) = NULL;
11921 unconst(mPeer) = NULL;
11922 LogFlowThisFuncLeave();
11923 return;
11924 }
11925
11926 MachineState_T lastState;
11927 {
11928 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11929 lastState = mData->mMachineState;
11930 }
11931 NOREF(lastState);
11932
11933#ifdef VBOX_WITH_USB
11934 // release all captured USB devices, but do this before requesting the locks below
11935 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11936 {
11937 /* Console::captureUSBDevices() is called in the VM process only after
11938 * setting the machine state to Starting or Restoring.
11939 * Console::detachAllUSBDevices() will be called upon successful
11940 * termination. So, we need to release USB devices only if there was
11941 * an abnormal termination of a running VM.
11942 *
11943 * This is identical to SessionMachine::DetachAllUSBDevices except
11944 * for the aAbnormal argument. */
11945 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11946 AssertComRC(rc);
11947 NOREF(rc);
11948
11949 USBProxyService *service = mParent->host()->usbProxyService();
11950 if (service)
11951 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11952 }
11953#endif /* VBOX_WITH_USB */
11954
11955 // we need to lock this object in uninit() because the lock is shared
11956 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11957 // and others need mParent lock, and USB needs host lock.
11958 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11959
11960#if 0
11961 // Trigger async cleanup tasks, avoid doing things here which are not
11962 // vital to be done immediately and maybe need more locks. This calls
11963 // Machine::unregisterMetrics().
11964 mParent->onMachineUninit(mPeer);
11965#else
11966 /*
11967 * It is safe to call Machine::unregisterMetrics() here because
11968 * PerformanceCollector::samplerCallback no longer accesses guest methods
11969 * holding the lock.
11970 */
11971 unregisterMetrics(mParent->performanceCollector(), mPeer);
11972#endif
11973 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11974 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11975 this, __PRETTY_FUNCTION__, mCollectorGuest));
11976 if (mCollectorGuest)
11977 {
11978 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11979 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11980 mCollectorGuest = NULL;
11981 }
11982
11983 if (aReason == Uninit::Abnormal)
11984 {
11985 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11986 Global::IsOnlineOrTransient(lastState)));
11987
11988 /* reset the state to Aborted */
11989 if (mData->mMachineState != MachineState_Aborted)
11990 setMachineState(MachineState_Aborted);
11991 }
11992
11993 // any machine settings modified?
11994 if (mData->flModifications)
11995 {
11996 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11997 rollback(false /* aNotify */);
11998 }
11999
12000 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12001 || !mConsoleTaskData.mSnapshot);
12002 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12003 {
12004 LogWarningThisFunc(("canceling failed save state request!\n"));
12005 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12006 }
12007 else if (!mConsoleTaskData.mSnapshot.isNull())
12008 {
12009 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12010
12011 /* delete all differencing hard disks created (this will also attach
12012 * their parents back by rolling back mMediaData) */
12013 rollbackMedia();
12014
12015 // delete the saved state file (it might have been already created)
12016 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12017 // think it's still in use
12018 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12019 mConsoleTaskData.mSnapshot->uninit();
12020 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12021 }
12022
12023 if (!mData->mSession.mType.isEmpty())
12024 {
12025 /* mType is not null when this machine's process has been started by
12026 * Machine::LaunchVMProcess(), therefore it is our child. We
12027 * need to queue the PID to reap the process (and avoid zombies on
12028 * Linux). */
12029 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12030 mParent->addProcessToReap(mData->mSession.mPID);
12031 }
12032
12033 mData->mSession.mPID = NIL_RTPROCESS;
12034
12035 if (aReason == Uninit::Unexpected)
12036 {
12037 /* Uninitialization didn't come from #checkForDeath(), so tell the
12038 * client watcher thread to update the set of machines that have open
12039 * sessions. */
12040 mParent->updateClientWatcher();
12041 }
12042
12043 /* uninitialize all remote controls */
12044 if (mData->mSession.mRemoteControls.size())
12045 {
12046 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12047 mData->mSession.mRemoteControls.size()));
12048
12049 Data::Session::RemoteControlList::iterator it =
12050 mData->mSession.mRemoteControls.begin();
12051 while (it != mData->mSession.mRemoteControls.end())
12052 {
12053 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12054 HRESULT rc = (*it)->Uninitialize();
12055 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12056 if (FAILED(rc))
12057 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12058 ++it;
12059 }
12060 mData->mSession.mRemoteControls.clear();
12061 }
12062
12063 /*
12064 * An expected uninitialization can come only from #checkForDeath().
12065 * Otherwise it means that something's gone really wrong (for example,
12066 * the Session implementation has released the VirtualBox reference
12067 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12068 * etc). However, it's also possible, that the client releases the IPC
12069 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12070 * but the VirtualBox release event comes first to the server process.
12071 * This case is practically possible, so we should not assert on an
12072 * unexpected uninit, just log a warning.
12073 */
12074
12075 if ((aReason == Uninit::Unexpected))
12076 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12077
12078 if (aReason != Uninit::Normal)
12079 {
12080 mData->mSession.mDirectControl.setNull();
12081 }
12082 else
12083 {
12084 /* this must be null here (see #OnSessionEnd()) */
12085 Assert(mData->mSession.mDirectControl.isNull());
12086 Assert(mData->mSession.mState == SessionState_Unlocking);
12087 Assert(!mData->mSession.mProgress.isNull());
12088 }
12089 if (mData->mSession.mProgress)
12090 {
12091 if (aReason == Uninit::Normal)
12092 mData->mSession.mProgress->notifyComplete(S_OK);
12093 else
12094 mData->mSession.mProgress->notifyComplete(E_FAIL,
12095 COM_IIDOF(ISession),
12096 getComponentName(),
12097 tr("The VM session was aborted"));
12098 mData->mSession.mProgress.setNull();
12099 }
12100
12101 /* remove the association between the peer machine and this session machine */
12102 Assert( (SessionMachine*)mData->mSession.mMachine == this
12103 || aReason == Uninit::Unexpected);
12104
12105 /* reset the rest of session data */
12106 mData->mSession.mMachine.setNull();
12107 mData->mSession.mState = SessionState_Unlocked;
12108 mData->mSession.mType.setNull();
12109
12110 /* close the interprocess semaphore before leaving the exclusive lock */
12111#if defined(RT_OS_WINDOWS)
12112 if (mIPCSem)
12113 ::CloseHandle(mIPCSem);
12114 mIPCSem = NULL;
12115#elif defined(RT_OS_OS2)
12116 if (mIPCSem != NULLHANDLE)
12117 ::DosCloseMutexSem(mIPCSem);
12118 mIPCSem = NULLHANDLE;
12119#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12120 if (mIPCSem >= 0)
12121 ::semctl(mIPCSem, 0, IPC_RMID);
12122 mIPCSem = -1;
12123# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12124 mIPCKey = "0";
12125# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12126#else
12127# error "Port me!"
12128#endif
12129
12130 /* fire an event */
12131 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12132
12133 uninitDataAndChildObjects();
12134
12135 /* free the essential data structure last */
12136 mData.free();
12137
12138 /* release the exclusive lock before setting the below two to NULL */
12139 multilock.release();
12140
12141 unconst(mParent) = NULL;
12142 unconst(mPeer) = NULL;
12143
12144 LogFlowThisFuncLeave();
12145}
12146
12147// util::Lockable interface
12148////////////////////////////////////////////////////////////////////////////////
12149
12150/**
12151 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12152 * with the primary Machine instance (mPeer).
12153 */
12154RWLockHandle *SessionMachine::lockHandle() const
12155{
12156 AssertReturn(mPeer != NULL, NULL);
12157 return mPeer->lockHandle();
12158}
12159
12160// IInternalMachineControl methods
12161////////////////////////////////////////////////////////////////////////////////
12162
12163/**
12164 * Passes collected guest statistics to performance collector object
12165 */
12166STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12167 ULONG aCpuKernel, ULONG aCpuIdle,
12168 ULONG aMemTotal, ULONG aMemFree,
12169 ULONG aMemBalloon, ULONG aMemShared,
12170 ULONG aMemCache, ULONG aPageTotal,
12171 ULONG aAllocVMM, ULONG aFreeVMM,
12172 ULONG aBalloonedVMM, ULONG aSharedVMM,
12173 ULONG aVmNetRx, ULONG aVmNetTx)
12174{
12175 if (mCollectorGuest)
12176 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12177 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12178 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12179 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12180
12181 return S_OK;
12182}
12183
12184/**
12185 * @note Locks this object for writing.
12186 */
12187STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12188{
12189 AutoCaller autoCaller(this);
12190 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12191
12192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12193
12194 mRemoveSavedState = aRemove;
12195
12196 return S_OK;
12197}
12198
12199/**
12200 * @note Locks the same as #setMachineState() does.
12201 */
12202STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12203{
12204 return setMachineState(aMachineState);
12205}
12206
12207/**
12208 * @note Locks this object for reading.
12209 */
12210STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12211{
12212 AutoCaller autoCaller(this);
12213 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12214
12215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12216
12217#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12218 mIPCSemName.cloneTo(aId);
12219 return S_OK;
12220#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12221# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12222 mIPCKey.cloneTo(aId);
12223# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12224 mData->m_strConfigFileFull.cloneTo(aId);
12225# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12226 return S_OK;
12227#else
12228# error "Port me!"
12229#endif
12230}
12231
12232/**
12233 * @note Locks this object for writing.
12234 */
12235STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12236{
12237 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12238 AutoCaller autoCaller(this);
12239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12240
12241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12242
12243 if (mData->mSession.mState != SessionState_Locked)
12244 return VBOX_E_INVALID_OBJECT_STATE;
12245
12246 if (!mData->mSession.mProgress.isNull())
12247 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12248
12249 LogFlowThisFunc(("returns S_OK.\n"));
12250 return S_OK;
12251}
12252
12253/**
12254 * @note Locks this object for writing.
12255 */
12256STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12257{
12258 AutoCaller autoCaller(this);
12259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12260
12261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12262
12263 if (mData->mSession.mState != SessionState_Locked)
12264 return VBOX_E_INVALID_OBJECT_STATE;
12265
12266 /* Finalize the LaunchVMProcess progress object. */
12267 if (mData->mSession.mProgress)
12268 {
12269 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12270 mData->mSession.mProgress.setNull();
12271 }
12272
12273 if (SUCCEEDED((HRESULT)iResult))
12274 {
12275#ifdef VBOX_WITH_RESOURCE_USAGE_API
12276 /* The VM has been powered up successfully, so it makes sense
12277 * now to offer the performance metrics for a running machine
12278 * object. Doing it earlier wouldn't be safe. */
12279 registerMetrics(mParent->performanceCollector(), mPeer,
12280 mData->mSession.mPID);
12281#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12282 }
12283
12284 return S_OK;
12285}
12286
12287/**
12288 * @note Locks this object for writing.
12289 */
12290STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12291{
12292 LogFlowThisFuncEnter();
12293
12294 CheckComArgOutPointerValid(aProgress);
12295
12296 AutoCaller autoCaller(this);
12297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12298
12299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12300
12301 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12302 E_FAIL);
12303
12304 /* create a progress object to track operation completion */
12305 ComObjPtr<Progress> pProgress;
12306 pProgress.createObject();
12307 pProgress->init(getVirtualBox(),
12308 static_cast<IMachine *>(this) /* aInitiator */,
12309 Bstr(tr("Stopping the virtual machine")).raw(),
12310 FALSE /* aCancelable */);
12311
12312 /* fill in the console task data */
12313 mConsoleTaskData.mLastState = mData->mMachineState;
12314 mConsoleTaskData.mProgress = pProgress;
12315
12316 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12317 setMachineState(MachineState_Stopping);
12318
12319 pProgress.queryInterfaceTo(aProgress);
12320
12321 return S_OK;
12322}
12323
12324/**
12325 * @note Locks this object for writing.
12326 */
12327STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12328{
12329 LogFlowThisFuncEnter();
12330
12331 AutoCaller autoCaller(this);
12332 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12333
12334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12335
12336 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12337 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12338 && mConsoleTaskData.mLastState != MachineState_Null,
12339 E_FAIL);
12340
12341 /*
12342 * On failure, set the state to the state we had when BeginPoweringDown()
12343 * was called (this is expected by Console::PowerDown() and the associated
12344 * task). On success the VM process already changed the state to
12345 * MachineState_PoweredOff, so no need to do anything.
12346 */
12347 if (FAILED(iResult))
12348 setMachineState(mConsoleTaskData.mLastState);
12349
12350 /* notify the progress object about operation completion */
12351 Assert(mConsoleTaskData.mProgress);
12352 if (SUCCEEDED(iResult))
12353 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12354 else
12355 {
12356 Utf8Str strErrMsg(aErrMsg);
12357 if (strErrMsg.length())
12358 mConsoleTaskData.mProgress->notifyComplete(iResult,
12359 COM_IIDOF(ISession),
12360 getComponentName(),
12361 strErrMsg.c_str());
12362 else
12363 mConsoleTaskData.mProgress->notifyComplete(iResult);
12364 }
12365
12366 /* clear out the temporary saved state data */
12367 mConsoleTaskData.mLastState = MachineState_Null;
12368 mConsoleTaskData.mProgress.setNull();
12369
12370 LogFlowThisFuncLeave();
12371 return S_OK;
12372}
12373
12374
12375/**
12376 * Goes through the USB filters of the given machine to see if the given
12377 * device matches any filter or not.
12378 *
12379 * @note Locks the same as USBController::hasMatchingFilter() does.
12380 */
12381STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12382 BOOL *aMatched,
12383 ULONG *aMaskedIfs)
12384{
12385 LogFlowThisFunc(("\n"));
12386
12387 CheckComArgNotNull(aUSBDevice);
12388 CheckComArgOutPointerValid(aMatched);
12389
12390 AutoCaller autoCaller(this);
12391 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12392
12393#ifdef VBOX_WITH_USB
12394 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12395#else
12396 NOREF(aUSBDevice);
12397 NOREF(aMaskedIfs);
12398 *aMatched = FALSE;
12399#endif
12400
12401 return S_OK;
12402}
12403
12404/**
12405 * @note Locks the same as Host::captureUSBDevice() does.
12406 */
12407STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12408{
12409 LogFlowThisFunc(("\n"));
12410
12411 AutoCaller autoCaller(this);
12412 AssertComRCReturnRC(autoCaller.rc());
12413
12414#ifdef VBOX_WITH_USB
12415 /* if captureDeviceForVM() fails, it must have set extended error info */
12416 clearError();
12417 MultiResult rc = mParent->host()->checkUSBProxyService();
12418 if (FAILED(rc)) return rc;
12419
12420 USBProxyService *service = mParent->host()->usbProxyService();
12421 AssertReturn(service, E_FAIL);
12422 return service->captureDeviceForVM(this, Guid(aId).ref());
12423#else
12424 NOREF(aId);
12425 return E_NOTIMPL;
12426#endif
12427}
12428
12429/**
12430 * @note Locks the same as Host::detachUSBDevice() does.
12431 */
12432STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12433{
12434 LogFlowThisFunc(("\n"));
12435
12436 AutoCaller autoCaller(this);
12437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12438
12439#ifdef VBOX_WITH_USB
12440 USBProxyService *service = mParent->host()->usbProxyService();
12441 AssertReturn(service, E_FAIL);
12442 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12443#else
12444 NOREF(aId);
12445 NOREF(aDone);
12446 return E_NOTIMPL;
12447#endif
12448}
12449
12450/**
12451 * Inserts all machine filters to the USB proxy service and then calls
12452 * Host::autoCaptureUSBDevices().
12453 *
12454 * Called by Console from the VM process upon VM startup.
12455 *
12456 * @note Locks what called methods lock.
12457 */
12458STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12459{
12460 LogFlowThisFunc(("\n"));
12461
12462 AutoCaller autoCaller(this);
12463 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12464
12465#ifdef VBOX_WITH_USB
12466 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12467 AssertComRC(rc);
12468 NOREF(rc);
12469
12470 USBProxyService *service = mParent->host()->usbProxyService();
12471 AssertReturn(service, E_FAIL);
12472 return service->autoCaptureDevicesForVM(this);
12473#else
12474 return S_OK;
12475#endif
12476}
12477
12478/**
12479 * Removes all machine filters from the USB proxy service and then calls
12480 * Host::detachAllUSBDevices().
12481 *
12482 * Called by Console from the VM process upon normal VM termination or by
12483 * SessionMachine::uninit() upon abnormal VM termination (from under the
12484 * Machine/SessionMachine lock).
12485 *
12486 * @note Locks what called methods lock.
12487 */
12488STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12489{
12490 LogFlowThisFunc(("\n"));
12491
12492 AutoCaller autoCaller(this);
12493 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12494
12495#ifdef VBOX_WITH_USB
12496 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12497 AssertComRC(rc);
12498 NOREF(rc);
12499
12500 USBProxyService *service = mParent->host()->usbProxyService();
12501 AssertReturn(service, E_FAIL);
12502 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12503#else
12504 NOREF(aDone);
12505 return S_OK;
12506#endif
12507}
12508
12509/**
12510 * @note Locks this object for writing.
12511 */
12512STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12513 IProgress **aProgress)
12514{
12515 LogFlowThisFuncEnter();
12516
12517 AssertReturn(aSession, E_INVALIDARG);
12518 AssertReturn(aProgress, E_INVALIDARG);
12519
12520 AutoCaller autoCaller(this);
12521
12522 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12523 /*
12524 * We don't assert below because it might happen that a non-direct session
12525 * informs us it is closed right after we've been uninitialized -- it's ok.
12526 */
12527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12528
12529 /* get IInternalSessionControl interface */
12530 ComPtr<IInternalSessionControl> control(aSession);
12531
12532 ComAssertRet(!control.isNull(), E_INVALIDARG);
12533
12534 /* Creating a Progress object requires the VirtualBox lock, and
12535 * thus locking it here is required by the lock order rules. */
12536 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12537
12538 if (control == mData->mSession.mDirectControl)
12539 {
12540 ComAssertRet(aProgress, E_POINTER);
12541
12542 /* The direct session is being normally closed by the client process
12543 * ----------------------------------------------------------------- */
12544
12545 /* go to the closing state (essential for all open*Session() calls and
12546 * for #checkForDeath()) */
12547 Assert(mData->mSession.mState == SessionState_Locked);
12548 mData->mSession.mState = SessionState_Unlocking;
12549
12550 /* set direct control to NULL to release the remote instance */
12551 mData->mSession.mDirectControl.setNull();
12552 LogFlowThisFunc(("Direct control is set to NULL\n"));
12553
12554 if (mData->mSession.mProgress)
12555 {
12556 /* finalize the progress, someone might wait if a frontend
12557 * closes the session before powering on the VM. */
12558 mData->mSession.mProgress->notifyComplete(E_FAIL,
12559 COM_IIDOF(ISession),
12560 getComponentName(),
12561 tr("The VM session was closed before any attempt to power it on"));
12562 mData->mSession.mProgress.setNull();
12563 }
12564
12565 /* Create the progress object the client will use to wait until
12566 * #checkForDeath() is called to uninitialize this session object after
12567 * it releases the IPC semaphore.
12568 * Note! Because we're "reusing" mProgress here, this must be a proxy
12569 * object just like for LaunchVMProcess. */
12570 Assert(mData->mSession.mProgress.isNull());
12571 ComObjPtr<ProgressProxy> progress;
12572 progress.createObject();
12573 ComPtr<IUnknown> pPeer(mPeer);
12574 progress->init(mParent, pPeer,
12575 Bstr(tr("Closing session")).raw(),
12576 FALSE /* aCancelable */);
12577 progress.queryInterfaceTo(aProgress);
12578 mData->mSession.mProgress = progress;
12579 }
12580 else
12581 {
12582 /* the remote session is being normally closed */
12583 Data::Session::RemoteControlList::iterator it =
12584 mData->mSession.mRemoteControls.begin();
12585 while (it != mData->mSession.mRemoteControls.end())
12586 {
12587 if (control == *it)
12588 break;
12589 ++it;
12590 }
12591 BOOL found = it != mData->mSession.mRemoteControls.end();
12592 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12593 E_INVALIDARG);
12594 // This MUST be erase(it), not remove(*it) as the latter triggers a
12595 // very nasty use after free due to the place where the value "lives".
12596 mData->mSession.mRemoteControls.erase(it);
12597 }
12598
12599 /* signal the client watcher thread, because the client is going away */
12600 mParent->updateClientWatcher();
12601
12602 LogFlowThisFuncLeave();
12603 return S_OK;
12604}
12605
12606/**
12607 * @note Locks this object for writing.
12608 */
12609STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12610{
12611 LogFlowThisFuncEnter();
12612
12613 CheckComArgOutPointerValid(aProgress);
12614 CheckComArgOutPointerValid(aStateFilePath);
12615
12616 AutoCaller autoCaller(this);
12617 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12618
12619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12620
12621 AssertReturn( mData->mMachineState == MachineState_Paused
12622 && mConsoleTaskData.mLastState == MachineState_Null
12623 && mConsoleTaskData.strStateFilePath.isEmpty(),
12624 E_FAIL);
12625
12626 /* create a progress object to track operation completion */
12627 ComObjPtr<Progress> pProgress;
12628 pProgress.createObject();
12629 pProgress->init(getVirtualBox(),
12630 static_cast<IMachine *>(this) /* aInitiator */,
12631 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12632 FALSE /* aCancelable */);
12633
12634 Utf8Str strStateFilePath;
12635 /* stateFilePath is null when the machine is not running */
12636 if (mData->mMachineState == MachineState_Paused)
12637 composeSavedStateFilename(strStateFilePath);
12638
12639 /* fill in the console task data */
12640 mConsoleTaskData.mLastState = mData->mMachineState;
12641 mConsoleTaskData.strStateFilePath = strStateFilePath;
12642 mConsoleTaskData.mProgress = pProgress;
12643
12644 /* set the state to Saving (this is expected by Console::SaveState()) */
12645 setMachineState(MachineState_Saving);
12646
12647 strStateFilePath.cloneTo(aStateFilePath);
12648 pProgress.queryInterfaceTo(aProgress);
12649
12650 return S_OK;
12651}
12652
12653/**
12654 * @note Locks mParent + this object for writing.
12655 */
12656STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12657{
12658 LogFlowThisFunc(("\n"));
12659
12660 AutoCaller autoCaller(this);
12661 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12662
12663 /* endSavingState() need mParent lock */
12664 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12665
12666 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12667 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12668 && mConsoleTaskData.mLastState != MachineState_Null
12669 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12670 E_FAIL);
12671
12672 /*
12673 * On failure, set the state to the state we had when BeginSavingState()
12674 * was called (this is expected by Console::SaveState() and the associated
12675 * task). On success the VM process already changed the state to
12676 * MachineState_Saved, so no need to do anything.
12677 */
12678 if (FAILED(iResult))
12679 setMachineState(mConsoleTaskData.mLastState);
12680
12681 return endSavingState(iResult, aErrMsg);
12682}
12683
12684/**
12685 * @note Locks this object for writing.
12686 */
12687STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12688{
12689 LogFlowThisFunc(("\n"));
12690
12691 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12692
12693 AutoCaller autoCaller(this);
12694 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12695
12696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12697
12698 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12699 || mData->mMachineState == MachineState_Teleported
12700 || mData->mMachineState == MachineState_Aborted
12701 , E_FAIL); /** @todo setError. */
12702
12703 Utf8Str stateFilePathFull = aSavedStateFile;
12704 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12705 if (RT_FAILURE(vrc))
12706 return setError(VBOX_E_FILE_ERROR,
12707 tr("Invalid saved state file path '%ls' (%Rrc)"),
12708 aSavedStateFile,
12709 vrc);
12710
12711 mSSData->strStateFilePath = stateFilePathFull;
12712
12713 /* The below setMachineState() will detect the state transition and will
12714 * update the settings file */
12715
12716 return setMachineState(MachineState_Saved);
12717}
12718
12719STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12720 ComSafeArrayOut(BSTR, aValues),
12721 ComSafeArrayOut(LONG64, aTimestamps),
12722 ComSafeArrayOut(BSTR, aFlags))
12723{
12724 LogFlowThisFunc(("\n"));
12725
12726#ifdef VBOX_WITH_GUEST_PROPS
12727 using namespace guestProp;
12728
12729 AutoCaller autoCaller(this);
12730 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12731
12732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12733
12734 CheckComArgOutSafeArrayPointerValid(aNames);
12735 CheckComArgOutSafeArrayPointerValid(aValues);
12736 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12737 CheckComArgOutSafeArrayPointerValid(aFlags);
12738
12739 size_t cEntries = mHWData->mGuestProperties.size();
12740 com::SafeArray<BSTR> names(cEntries);
12741 com::SafeArray<BSTR> values(cEntries);
12742 com::SafeArray<LONG64> timestamps(cEntries);
12743 com::SafeArray<BSTR> flags(cEntries);
12744 unsigned i = 0;
12745 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12746 it != mHWData->mGuestProperties.end();
12747 ++it)
12748 {
12749 char szFlags[MAX_FLAGS_LEN + 1];
12750 it->strName.cloneTo(&names[i]);
12751 it->strValue.cloneTo(&values[i]);
12752 timestamps[i] = it->mTimestamp;
12753 /* If it is NULL, keep it NULL. */
12754 if (it->mFlags)
12755 {
12756 writeFlags(it->mFlags, szFlags);
12757 Bstr(szFlags).cloneTo(&flags[i]);
12758 }
12759 else
12760 flags[i] = NULL;
12761 ++i;
12762 }
12763 names.detachTo(ComSafeArrayOutArg(aNames));
12764 values.detachTo(ComSafeArrayOutArg(aValues));
12765 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12766 flags.detachTo(ComSafeArrayOutArg(aFlags));
12767 return S_OK;
12768#else
12769 ReturnComNotImplemented();
12770#endif
12771}
12772
12773STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12774 IN_BSTR aValue,
12775 LONG64 aTimestamp,
12776 IN_BSTR aFlags)
12777{
12778 LogFlowThisFunc(("\n"));
12779
12780#ifdef VBOX_WITH_GUEST_PROPS
12781 using namespace guestProp;
12782
12783 CheckComArgStrNotEmptyOrNull(aName);
12784 CheckComArgNotNull(aValue);
12785 CheckComArgNotNull(aFlags);
12786
12787 try
12788 {
12789 /*
12790 * Convert input up front.
12791 */
12792 Utf8Str utf8Name(aName);
12793 uint32_t fFlags = NILFLAG;
12794 if (aFlags)
12795 {
12796 Utf8Str utf8Flags(aFlags);
12797 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12798 AssertRCReturn(vrc, E_INVALIDARG);
12799 }
12800
12801 /*
12802 * Now grab the object lock, validate the state and do the update.
12803 */
12804 AutoCaller autoCaller(this);
12805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12806
12807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12808
12809 switch (mData->mMachineState)
12810 {
12811 case MachineState_Paused:
12812 case MachineState_Running:
12813 case MachineState_Teleporting:
12814 case MachineState_TeleportingPausedVM:
12815 case MachineState_LiveSnapshotting:
12816 case MachineState_DeletingSnapshotOnline:
12817 case MachineState_DeletingSnapshotPaused:
12818 case MachineState_Saving:
12819 break;
12820
12821 default:
12822#ifndef DEBUG_sunlover
12823 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12824 VBOX_E_INVALID_VM_STATE);
12825#else
12826 return VBOX_E_INVALID_VM_STATE;
12827#endif
12828 }
12829
12830 setModified(IsModified_MachineData);
12831 mHWData.backup();
12832
12833 /** @todo r=bird: The careful memory handling doesn't work out here because
12834 * the catch block won't undo any damage we've done. So, if push_back throws
12835 * bad_alloc then you've lost the value.
12836 *
12837 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12838 * since values that changes actually bubbles to the end of the list. Using
12839 * something that has an efficient lookup and can tolerate a bit of updates
12840 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12841 * combination of RTStrCache (for sharing names and getting uniqueness into
12842 * the bargain) and hash/tree is another. */
12843 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12844 iter != mHWData->mGuestProperties.end();
12845 ++iter)
12846 if (utf8Name == iter->strName)
12847 {
12848 mHWData->mGuestProperties.erase(iter);
12849 mData->mGuestPropertiesModified = TRUE;
12850 break;
12851 }
12852 if (aValue != NULL)
12853 {
12854 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12855 mHWData->mGuestProperties.push_back(property);
12856 mData->mGuestPropertiesModified = TRUE;
12857 }
12858
12859 /*
12860 * Send a callback notification if appropriate
12861 */
12862 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12863 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12864 RTSTR_MAX,
12865 utf8Name.c_str(),
12866 RTSTR_MAX, NULL)
12867 )
12868 {
12869 alock.release();
12870
12871 mParent->onGuestPropertyChange(mData->mUuid,
12872 aName,
12873 aValue,
12874 aFlags);
12875 }
12876 }
12877 catch (...)
12878 {
12879 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12880 }
12881 return S_OK;
12882#else
12883 ReturnComNotImplemented();
12884#endif
12885}
12886
12887STDMETHODIMP SessionMachine::LockMedia()
12888{
12889 AutoCaller autoCaller(this);
12890 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12891
12892 AutoMultiWriteLock2 alock(this->lockHandle(),
12893 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12894
12895 AssertReturn( mData->mMachineState == MachineState_Starting
12896 || mData->mMachineState == MachineState_Restoring
12897 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
12898
12899 clearError();
12900 alock.release();
12901 return lockMedia();
12902}
12903
12904STDMETHODIMP SessionMachine::UnlockMedia()
12905{
12906 unlockMedia();
12907 return S_OK;
12908}
12909
12910STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12911 IMediumAttachment **aNewAttachment)
12912{
12913 CheckComArgNotNull(aAttachment);
12914 CheckComArgOutPointerValid(aNewAttachment);
12915
12916 AutoCaller autoCaller(this);
12917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12918
12919 // request the host lock first, since might be calling Host methods for getting host drives;
12920 // next, protect the media tree all the while we're in here, as well as our member variables
12921 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12922 this->lockHandle(),
12923 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12924
12925 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12926
12927 Bstr ctrlName;
12928 LONG lPort;
12929 LONG lDevice;
12930 bool fTempEject;
12931 {
12932 AutoCaller autoAttachCaller(this);
12933 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12934
12935 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12936
12937 /* Need to query the details first, as the IMediumAttachment reference
12938 * might be to the original settings, which we are going to change. */
12939 ctrlName = pAttach->getControllerName();
12940 lPort = pAttach->getPort();
12941 lDevice = pAttach->getDevice();
12942 fTempEject = pAttach->getTempEject();
12943 }
12944
12945 if (!fTempEject)
12946 {
12947 /* Remember previously mounted medium. The medium before taking the
12948 * backup is not necessarily the same thing. */
12949 ComObjPtr<Medium> oldmedium;
12950 oldmedium = pAttach->getMedium();
12951
12952 setModified(IsModified_Storage);
12953 mMediaData.backup();
12954
12955 // The backup operation makes the pAttach reference point to the
12956 // old settings. Re-get the correct reference.
12957 pAttach = findAttachment(mMediaData->mAttachments,
12958 ctrlName.raw(),
12959 lPort,
12960 lDevice);
12961
12962 {
12963 AutoCaller autoAttachCaller(this);
12964 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12965
12966 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12967 if (!oldmedium.isNull())
12968 oldmedium->removeBackReference(mData->mUuid);
12969
12970 pAttach->updateMedium(NULL);
12971 pAttach->updateEjected();
12972 }
12973
12974 setModified(IsModified_Storage);
12975 }
12976 else
12977 {
12978 {
12979 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12980 pAttach->updateEjected();
12981 }
12982 }
12983
12984 pAttach.queryInterfaceTo(aNewAttachment);
12985
12986 return S_OK;
12987}
12988
12989// public methods only for internal purposes
12990/////////////////////////////////////////////////////////////////////////////
12991
12992/**
12993 * Called from the client watcher thread to check for expected or unexpected
12994 * death of the client process that has a direct session to this machine.
12995 *
12996 * On Win32 and on OS/2, this method is called only when we've got the
12997 * mutex (i.e. the client has either died or terminated normally) so it always
12998 * returns @c true (the client is terminated, the session machine is
12999 * uninitialized).
13000 *
13001 * On other platforms, the method returns @c true if the client process has
13002 * terminated normally or abnormally and the session machine was uninitialized,
13003 * and @c false if the client process is still alive.
13004 *
13005 * @note Locks this object for writing.
13006 */
13007bool SessionMachine::checkForDeath()
13008{
13009 Uninit::Reason reason;
13010 bool terminated = false;
13011
13012 /* Enclose autoCaller with a block because calling uninit() from under it
13013 * will deadlock. */
13014 {
13015 AutoCaller autoCaller(this);
13016 if (!autoCaller.isOk())
13017 {
13018 /* return true if not ready, to cause the client watcher to exclude
13019 * the corresponding session from watching */
13020 LogFlowThisFunc(("Already uninitialized!\n"));
13021 return true;
13022 }
13023
13024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13025
13026 /* Determine the reason of death: if the session state is Closing here,
13027 * everything is fine. Otherwise it means that the client did not call
13028 * OnSessionEnd() before it released the IPC semaphore. This may happen
13029 * either because the client process has abnormally terminated, or
13030 * because it simply forgot to call ISession::Close() before exiting. We
13031 * threat the latter also as an abnormal termination (see
13032 * Session::uninit() for details). */
13033 reason = mData->mSession.mState == SessionState_Unlocking ?
13034 Uninit::Normal :
13035 Uninit::Abnormal;
13036
13037#if defined(RT_OS_WINDOWS)
13038
13039 AssertMsg(mIPCSem, ("semaphore must be created"));
13040
13041 /* release the IPC mutex */
13042 ::ReleaseMutex(mIPCSem);
13043
13044 terminated = true;
13045
13046#elif defined(RT_OS_OS2)
13047
13048 AssertMsg(mIPCSem, ("semaphore must be created"));
13049
13050 /* release the IPC mutex */
13051 ::DosReleaseMutexSem(mIPCSem);
13052
13053 terminated = true;
13054
13055#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13056
13057 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13058
13059 int val = ::semctl(mIPCSem, 0, GETVAL);
13060 if (val > 0)
13061 {
13062 /* the semaphore is signaled, meaning the session is terminated */
13063 terminated = true;
13064 }
13065
13066#else
13067# error "Port me!"
13068#endif
13069
13070 } /* AutoCaller block */
13071
13072 if (terminated)
13073 uninit(reason);
13074
13075 return terminated;
13076}
13077
13078/**
13079 * @note Locks this object for reading.
13080 */
13081HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13082{
13083 LogFlowThisFunc(("\n"));
13084
13085 AutoCaller autoCaller(this);
13086 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13087
13088 ComPtr<IInternalSessionControl> directControl;
13089 {
13090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13091 directControl = mData->mSession.mDirectControl;
13092 }
13093
13094 /* ignore notifications sent after #OnSessionEnd() is called */
13095 if (!directControl)
13096 return S_OK;
13097
13098 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13099}
13100
13101/**
13102 * @note Locks this object for reading.
13103 */
13104HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13105 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13106{
13107 LogFlowThisFunc(("\n"));
13108
13109 AutoCaller autoCaller(this);
13110 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13111
13112 ComPtr<IInternalSessionControl> directControl;
13113 {
13114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13115 directControl = mData->mSession.mDirectControl;
13116 }
13117
13118 /* ignore notifications sent after #OnSessionEnd() is called */
13119 if (!directControl)
13120 return S_OK;
13121 /*
13122 * instead acting like callback we ask IVirtualBox deliver corresponding event
13123 */
13124
13125 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13126 return S_OK;
13127}
13128
13129/**
13130 * @note Locks this object for reading.
13131 */
13132HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13133{
13134 LogFlowThisFunc(("\n"));
13135
13136 AutoCaller autoCaller(this);
13137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13138
13139 ComPtr<IInternalSessionControl> directControl;
13140 {
13141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13142 directControl = mData->mSession.mDirectControl;
13143 }
13144
13145 /* ignore notifications sent after #OnSessionEnd() is called */
13146 if (!directControl)
13147 return S_OK;
13148
13149 return directControl->OnSerialPortChange(serialPort);
13150}
13151
13152/**
13153 * @note Locks this object for reading.
13154 */
13155HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13156{
13157 LogFlowThisFunc(("\n"));
13158
13159 AutoCaller autoCaller(this);
13160 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13161
13162 ComPtr<IInternalSessionControl> directControl;
13163 {
13164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13165 directControl = mData->mSession.mDirectControl;
13166 }
13167
13168 /* ignore notifications sent after #OnSessionEnd() is called */
13169 if (!directControl)
13170 return S_OK;
13171
13172 return directControl->OnParallelPortChange(parallelPort);
13173}
13174
13175/**
13176 * @note Locks this object for reading.
13177 */
13178HRESULT SessionMachine::onStorageControllerChange()
13179{
13180 LogFlowThisFunc(("\n"));
13181
13182 AutoCaller autoCaller(this);
13183 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13184
13185 ComPtr<IInternalSessionControl> directControl;
13186 {
13187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13188 directControl = mData->mSession.mDirectControl;
13189 }
13190
13191 /* ignore notifications sent after #OnSessionEnd() is called */
13192 if (!directControl)
13193 return S_OK;
13194
13195 return directControl->OnStorageControllerChange();
13196}
13197
13198/**
13199 * @note Locks this object for reading.
13200 */
13201HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13202{
13203 LogFlowThisFunc(("\n"));
13204
13205 AutoCaller autoCaller(this);
13206 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13207
13208 ComPtr<IInternalSessionControl> directControl;
13209 {
13210 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13211 directControl = mData->mSession.mDirectControl;
13212 }
13213
13214 /* ignore notifications sent after #OnSessionEnd() is called */
13215 if (!directControl)
13216 return S_OK;
13217
13218 return directControl->OnMediumChange(aAttachment, aForce);
13219}
13220
13221/**
13222 * @note Locks this object for reading.
13223 */
13224HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13225{
13226 LogFlowThisFunc(("\n"));
13227
13228 AutoCaller autoCaller(this);
13229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13230
13231 ComPtr<IInternalSessionControl> directControl;
13232 {
13233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13234 directControl = mData->mSession.mDirectControl;
13235 }
13236
13237 /* ignore notifications sent after #OnSessionEnd() is called */
13238 if (!directControl)
13239 return S_OK;
13240
13241 return directControl->OnCPUChange(aCPU, aRemove);
13242}
13243
13244HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13245{
13246 LogFlowThisFunc(("\n"));
13247
13248 AutoCaller autoCaller(this);
13249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13250
13251 ComPtr<IInternalSessionControl> directControl;
13252 {
13253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13254 directControl = mData->mSession.mDirectControl;
13255 }
13256
13257 /* ignore notifications sent after #OnSessionEnd() is called */
13258 if (!directControl)
13259 return S_OK;
13260
13261 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13262}
13263
13264/**
13265 * @note Locks this object for reading.
13266 */
13267HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13268{
13269 LogFlowThisFunc(("\n"));
13270
13271 AutoCaller autoCaller(this);
13272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13273
13274 ComPtr<IInternalSessionControl> directControl;
13275 {
13276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13277 directControl = mData->mSession.mDirectControl;
13278 }
13279
13280 /* ignore notifications sent after #OnSessionEnd() is called */
13281 if (!directControl)
13282 return S_OK;
13283
13284 return directControl->OnVRDEServerChange(aRestart);
13285}
13286
13287/**
13288 * @note Locks this object for reading.
13289 */
13290HRESULT SessionMachine::onUSBControllerChange()
13291{
13292 LogFlowThisFunc(("\n"));
13293
13294 AutoCaller autoCaller(this);
13295 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13296
13297 ComPtr<IInternalSessionControl> directControl;
13298 {
13299 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13300 directControl = mData->mSession.mDirectControl;
13301 }
13302
13303 /* ignore notifications sent after #OnSessionEnd() is called */
13304 if (!directControl)
13305 return S_OK;
13306
13307 return directControl->OnUSBControllerChange();
13308}
13309
13310/**
13311 * @note Locks this object for reading.
13312 */
13313HRESULT SessionMachine::onSharedFolderChange()
13314{
13315 LogFlowThisFunc(("\n"));
13316
13317 AutoCaller autoCaller(this);
13318 AssertComRCReturnRC(autoCaller.rc());
13319
13320 ComPtr<IInternalSessionControl> directControl;
13321 {
13322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13323 directControl = mData->mSession.mDirectControl;
13324 }
13325
13326 /* ignore notifications sent after #OnSessionEnd() is called */
13327 if (!directControl)
13328 return S_OK;
13329
13330 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13331}
13332
13333/**
13334 * @note Locks this object for reading.
13335 */
13336HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13337{
13338 LogFlowThisFunc(("\n"));
13339
13340 AutoCaller autoCaller(this);
13341 AssertComRCReturnRC(autoCaller.rc());
13342
13343 ComPtr<IInternalSessionControl> directControl;
13344 {
13345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13346 directControl = mData->mSession.mDirectControl;
13347 }
13348
13349 /* ignore notifications sent after #OnSessionEnd() is called */
13350 if (!directControl)
13351 return S_OK;
13352
13353 return directControl->OnClipboardModeChange(aClipboardMode);
13354}
13355
13356/**
13357 * @note Locks this object for reading.
13358 */
13359HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13360{
13361 LogFlowThisFunc(("\n"));
13362
13363 AutoCaller autoCaller(this);
13364 AssertComRCReturnRC(autoCaller.rc());
13365
13366 ComPtr<IInternalSessionControl> directControl;
13367 {
13368 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13369 directControl = mData->mSession.mDirectControl;
13370 }
13371
13372 /* ignore notifications sent after #OnSessionEnd() is called */
13373 if (!directControl)
13374 return S_OK;
13375
13376 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13377}
13378
13379/**
13380 * @note Locks this object for reading.
13381 */
13382HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13383{
13384 LogFlowThisFunc(("\n"));
13385
13386 AutoCaller autoCaller(this);
13387 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13388
13389 ComPtr<IInternalSessionControl> directControl;
13390 {
13391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13392 directControl = mData->mSession.mDirectControl;
13393 }
13394
13395 /* ignore notifications sent after #OnSessionEnd() is called */
13396 if (!directControl)
13397 return S_OK;
13398
13399 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13400}
13401
13402/**
13403 * @note Locks this object for reading.
13404 */
13405HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13406{
13407 LogFlowThisFunc(("\n"));
13408
13409 AutoCaller autoCaller(this);
13410 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13411
13412 ComPtr<IInternalSessionControl> directControl;
13413 {
13414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13415 directControl = mData->mSession.mDirectControl;
13416 }
13417
13418 /* ignore notifications sent after #OnSessionEnd() is called */
13419 if (!directControl)
13420 return S_OK;
13421
13422 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13423}
13424
13425/**
13426 * Returns @c true if this machine's USB controller reports it has a matching
13427 * filter for the given USB device and @c false otherwise.
13428 *
13429 * @note locks this object for reading.
13430 */
13431bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13432{
13433 AutoCaller autoCaller(this);
13434 /* silently return if not ready -- this method may be called after the
13435 * direct machine session has been called */
13436 if (!autoCaller.isOk())
13437 return false;
13438
13439#ifdef VBOX_WITH_USB
13440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13441
13442 switch (mData->mMachineState)
13443 {
13444 case MachineState_Starting:
13445 case MachineState_Restoring:
13446 case MachineState_TeleportingIn:
13447 case MachineState_Paused:
13448 case MachineState_Running:
13449 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13450 * elsewhere... */
13451 alock.release();
13452 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13453 default: break;
13454 }
13455#else
13456 NOREF(aDevice);
13457 NOREF(aMaskedIfs);
13458#endif
13459 return false;
13460}
13461
13462/**
13463 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13464 */
13465HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13466 IVirtualBoxErrorInfo *aError,
13467 ULONG aMaskedIfs)
13468{
13469 LogFlowThisFunc(("\n"));
13470
13471 AutoCaller autoCaller(this);
13472
13473 /* This notification may happen after the machine object has been
13474 * uninitialized (the session was closed), so don't assert. */
13475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13476
13477 ComPtr<IInternalSessionControl> directControl;
13478 {
13479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13480 directControl = mData->mSession.mDirectControl;
13481 }
13482
13483 /* fail on notifications sent after #OnSessionEnd() is called, it is
13484 * expected by the caller */
13485 if (!directControl)
13486 return E_FAIL;
13487
13488 /* No locks should be held at this point. */
13489 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13490 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13491
13492 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13493}
13494
13495/**
13496 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13497 */
13498HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13499 IVirtualBoxErrorInfo *aError)
13500{
13501 LogFlowThisFunc(("\n"));
13502
13503 AutoCaller autoCaller(this);
13504
13505 /* This notification may happen after the machine object has been
13506 * uninitialized (the session was closed), so don't assert. */
13507 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13508
13509 ComPtr<IInternalSessionControl> directControl;
13510 {
13511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13512 directControl = mData->mSession.mDirectControl;
13513 }
13514
13515 /* fail on notifications sent after #OnSessionEnd() is called, it is
13516 * expected by the caller */
13517 if (!directControl)
13518 return E_FAIL;
13519
13520 /* No locks should be held at this point. */
13521 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13522 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13523
13524 return directControl->OnUSBDeviceDetach(aId, aError);
13525}
13526
13527// protected methods
13528/////////////////////////////////////////////////////////////////////////////
13529
13530/**
13531 * Helper method to finalize saving the state.
13532 *
13533 * @note Must be called from under this object's lock.
13534 *
13535 * @param aRc S_OK if the snapshot has been taken successfully
13536 * @param aErrMsg human readable error message for failure
13537 *
13538 * @note Locks mParent + this objects for writing.
13539 */
13540HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13541{
13542 LogFlowThisFuncEnter();
13543
13544 AutoCaller autoCaller(this);
13545 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13546
13547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13548
13549 HRESULT rc = S_OK;
13550
13551 if (SUCCEEDED(aRc))
13552 {
13553 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13554
13555 /* save all VM settings */
13556 rc = saveSettings(NULL);
13557 // no need to check whether VirtualBox.xml needs saving also since
13558 // we can't have a name change pending at this point
13559 }
13560 else
13561 {
13562 // delete the saved state file (it might have been already created);
13563 // we need not check whether this is shared with a snapshot here because
13564 // we certainly created this saved state file here anew
13565 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13566 }
13567
13568 /* notify the progress object about operation completion */
13569 Assert(mConsoleTaskData.mProgress);
13570 if (SUCCEEDED(aRc))
13571 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13572 else
13573 {
13574 if (aErrMsg.length())
13575 mConsoleTaskData.mProgress->notifyComplete(aRc,
13576 COM_IIDOF(ISession),
13577 getComponentName(),
13578 aErrMsg.c_str());
13579 else
13580 mConsoleTaskData.mProgress->notifyComplete(aRc);
13581 }
13582
13583 /* clear out the temporary saved state data */
13584 mConsoleTaskData.mLastState = MachineState_Null;
13585 mConsoleTaskData.strStateFilePath.setNull();
13586 mConsoleTaskData.mProgress.setNull();
13587
13588 LogFlowThisFuncLeave();
13589 return rc;
13590}
13591
13592/**
13593 * Deletes the given file if it is no longer in use by either the current machine state
13594 * (if the machine is "saved") or any of the machine's snapshots.
13595 *
13596 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13597 * but is different for each SnapshotMachine. When calling this, the order of calling this
13598 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13599 * is therefore critical. I know, it's all rather messy.
13600 *
13601 * @param strStateFile
13602 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13603 */
13604void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13605 Snapshot *pSnapshotToIgnore)
13606{
13607 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13608 if ( (strStateFile.isNotEmpty())
13609 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13610 )
13611 // ... and it must also not be shared with other snapshots
13612 if ( !mData->mFirstSnapshot
13613 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13614 // this checks the SnapshotMachine's state file paths
13615 )
13616 RTFileDelete(strStateFile.c_str());
13617}
13618
13619/**
13620 * Locks the attached media.
13621 *
13622 * All attached hard disks are locked for writing and DVD/floppy are locked for
13623 * reading. Parents of attached hard disks (if any) are locked for reading.
13624 *
13625 * This method also performs accessibility check of all media it locks: if some
13626 * media is inaccessible, the method will return a failure and a bunch of
13627 * extended error info objects per each inaccessible medium.
13628 *
13629 * Note that this method is atomic: if it returns a success, all media are
13630 * locked as described above; on failure no media is locked at all (all
13631 * succeeded individual locks will be undone).
13632 *
13633 * The caller is responsible for doing the necessary state sanity checks.
13634 *
13635 * The locks made by this method must be undone by calling #unlockMedia() when
13636 * no more needed.
13637 */
13638HRESULT SessionMachine::lockMedia()
13639{
13640 AutoCaller autoCaller(this);
13641 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13642
13643 AutoMultiWriteLock2 alock(this->lockHandle(),
13644 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13645
13646 /* bail out if trying to lock things with already set up locking */
13647 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13648
13649 MultiResult mrc(S_OK);
13650
13651 /* Collect locking information for all medium objects attached to the VM. */
13652 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13653 it != mMediaData->mAttachments.end();
13654 ++it)
13655 {
13656 MediumAttachment* pAtt = *it;
13657 DeviceType_T devType = pAtt->getType();
13658 Medium *pMedium = pAtt->getMedium();
13659
13660 MediumLockList *pMediumLockList(new MediumLockList());
13661 // There can be attachments without a medium (floppy/dvd), and thus
13662 // it's impossible to create a medium lock list. It still makes sense
13663 // to have the empty medium lock list in the map in case a medium is
13664 // attached later.
13665 if (pMedium != NULL)
13666 {
13667 MediumType_T mediumType = pMedium->getType();
13668 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13669 || mediumType == MediumType_Shareable;
13670 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13671
13672 alock.release();
13673 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13674 !fIsReadOnlyLock /* fMediumLockWrite */,
13675 NULL,
13676 *pMediumLockList);
13677 alock.acquire();
13678 if (FAILED(mrc))
13679 {
13680 delete pMediumLockList;
13681 mData->mSession.mLockedMedia.Clear();
13682 break;
13683 }
13684 }
13685
13686 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13687 if (FAILED(rc))
13688 {
13689 mData->mSession.mLockedMedia.Clear();
13690 mrc = setError(rc,
13691 tr("Collecting locking information for all attached media failed"));
13692 break;
13693 }
13694 }
13695
13696 if (SUCCEEDED(mrc))
13697 {
13698 /* Now lock all media. If this fails, nothing is locked. */
13699 alock.release();
13700 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13701 alock.acquire();
13702 if (FAILED(rc))
13703 {
13704 mrc = setError(rc,
13705 tr("Locking of attached media failed"));
13706 }
13707 }
13708
13709 return mrc;
13710}
13711
13712/**
13713 * Undoes the locks made by by #lockMedia().
13714 */
13715void SessionMachine::unlockMedia()
13716{
13717 AutoCaller autoCaller(this);
13718 AssertComRCReturnVoid(autoCaller.rc());
13719
13720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13721
13722 /* we may be holding important error info on the current thread;
13723 * preserve it */
13724 ErrorInfoKeeper eik;
13725
13726 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13727 AssertComRC(rc);
13728}
13729
13730/**
13731 * Helper to change the machine state (reimplementation).
13732 *
13733 * @note Locks this object for writing.
13734 * @note This method must not call saveSettings or SaveSettings, otherwise
13735 * it can cause crashes in random places due to unexpectedly committing
13736 * the current settings. The caller is responsible for that. The call
13737 * to saveStateSettings is fine, because this method does not commit.
13738 */
13739HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13740{
13741 LogFlowThisFuncEnter();
13742 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13743
13744 AutoCaller autoCaller(this);
13745 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13746
13747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13748
13749 MachineState_T oldMachineState = mData->mMachineState;
13750
13751 AssertMsgReturn(oldMachineState != aMachineState,
13752 ("oldMachineState=%s, aMachineState=%s\n",
13753 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13754 E_FAIL);
13755
13756 HRESULT rc = S_OK;
13757
13758 int stsFlags = 0;
13759 bool deleteSavedState = false;
13760
13761 /* detect some state transitions */
13762
13763 if ( ( oldMachineState == MachineState_Saved
13764 && aMachineState == MachineState_Restoring)
13765 || ( ( oldMachineState == MachineState_PoweredOff
13766 || oldMachineState == MachineState_Teleported
13767 || oldMachineState == MachineState_Aborted
13768 )
13769 && ( aMachineState == MachineState_TeleportingIn
13770 || aMachineState == MachineState_Starting
13771 )
13772 )
13773 )
13774 {
13775 /* The EMT thread is about to start */
13776
13777 /* Nothing to do here for now... */
13778
13779 /// @todo NEWMEDIA don't let mDVDDrive and other children
13780 /// change anything when in the Starting/Restoring state
13781 }
13782 else if ( ( oldMachineState == MachineState_Running
13783 || oldMachineState == MachineState_Paused
13784 || oldMachineState == MachineState_Teleporting
13785 || oldMachineState == MachineState_LiveSnapshotting
13786 || oldMachineState == MachineState_Stuck
13787 || oldMachineState == MachineState_Starting
13788 || oldMachineState == MachineState_Stopping
13789 || oldMachineState == MachineState_Saving
13790 || oldMachineState == MachineState_Restoring
13791 || oldMachineState == MachineState_TeleportingPausedVM
13792 || oldMachineState == MachineState_TeleportingIn
13793 )
13794 && ( aMachineState == MachineState_PoweredOff
13795 || aMachineState == MachineState_Saved
13796 || aMachineState == MachineState_Teleported
13797 || aMachineState == MachineState_Aborted
13798 )
13799 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13800 * snapshot */
13801 && ( mConsoleTaskData.mSnapshot.isNull()
13802 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13803 )
13804 )
13805 {
13806 /* The EMT thread has just stopped, unlock attached media. Note that as
13807 * opposed to locking that is done from Console, we do unlocking here
13808 * because the VM process may have aborted before having a chance to
13809 * properly unlock all media it locked. */
13810
13811 unlockMedia();
13812 }
13813
13814 if (oldMachineState == MachineState_Restoring)
13815 {
13816 if (aMachineState != MachineState_Saved)
13817 {
13818 /*
13819 * delete the saved state file once the machine has finished
13820 * restoring from it (note that Console sets the state from
13821 * Restoring to Saved if the VM couldn't restore successfully,
13822 * to give the user an ability to fix an error and retry --
13823 * we keep the saved state file in this case)
13824 */
13825 deleteSavedState = true;
13826 }
13827 }
13828 else if ( oldMachineState == MachineState_Saved
13829 && ( aMachineState == MachineState_PoweredOff
13830 || aMachineState == MachineState_Aborted
13831 || aMachineState == MachineState_Teleported
13832 )
13833 )
13834 {
13835 /*
13836 * delete the saved state after Console::ForgetSavedState() is called
13837 * or if the VM process (owning a direct VM session) crashed while the
13838 * VM was Saved
13839 */
13840
13841 /// @todo (dmik)
13842 // Not sure that deleting the saved state file just because of the
13843 // client death before it attempted to restore the VM is a good
13844 // thing. But when it crashes we need to go to the Aborted state
13845 // which cannot have the saved state file associated... The only
13846 // way to fix this is to make the Aborted condition not a VM state
13847 // but a bool flag: i.e., when a crash occurs, set it to true and
13848 // change the state to PoweredOff or Saved depending on the
13849 // saved state presence.
13850
13851 deleteSavedState = true;
13852 mData->mCurrentStateModified = TRUE;
13853 stsFlags |= SaveSTS_CurStateModified;
13854 }
13855
13856 if ( aMachineState == MachineState_Starting
13857 || aMachineState == MachineState_Restoring
13858 || aMachineState == MachineState_TeleportingIn
13859 )
13860 {
13861 /* set the current state modified flag to indicate that the current
13862 * state is no more identical to the state in the
13863 * current snapshot */
13864 if (!mData->mCurrentSnapshot.isNull())
13865 {
13866 mData->mCurrentStateModified = TRUE;
13867 stsFlags |= SaveSTS_CurStateModified;
13868 }
13869 }
13870
13871 if (deleteSavedState)
13872 {
13873 if (mRemoveSavedState)
13874 {
13875 Assert(!mSSData->strStateFilePath.isEmpty());
13876
13877 // it is safe to delete the saved state file if ...
13878 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13879 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13880 // ... none of the snapshots share the saved state file
13881 )
13882 RTFileDelete(mSSData->strStateFilePath.c_str());
13883 }
13884
13885 mSSData->strStateFilePath.setNull();
13886 stsFlags |= SaveSTS_StateFilePath;
13887 }
13888
13889 /* redirect to the underlying peer machine */
13890 mPeer->setMachineState(aMachineState);
13891
13892 if ( aMachineState == MachineState_PoweredOff
13893 || aMachineState == MachineState_Teleported
13894 || aMachineState == MachineState_Aborted
13895 || aMachineState == MachineState_Saved)
13896 {
13897 /* the machine has stopped execution
13898 * (or the saved state file was adopted) */
13899 stsFlags |= SaveSTS_StateTimeStamp;
13900 }
13901
13902 if ( ( oldMachineState == MachineState_PoweredOff
13903 || oldMachineState == MachineState_Aborted
13904 || oldMachineState == MachineState_Teleported
13905 )
13906 && aMachineState == MachineState_Saved)
13907 {
13908 /* the saved state file was adopted */
13909 Assert(!mSSData->strStateFilePath.isEmpty());
13910 stsFlags |= SaveSTS_StateFilePath;
13911 }
13912
13913#ifdef VBOX_WITH_GUEST_PROPS
13914 if ( aMachineState == MachineState_PoweredOff
13915 || aMachineState == MachineState_Aborted
13916 || aMachineState == MachineState_Teleported)
13917 {
13918 /* Make sure any transient guest properties get removed from the
13919 * property store on shutdown. */
13920
13921 HWData::GuestPropertyList::iterator it;
13922 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13923 if (!fNeedsSaving)
13924 for (it = mHWData->mGuestProperties.begin();
13925 it != mHWData->mGuestProperties.end(); ++it)
13926 if ( (it->mFlags & guestProp::TRANSIENT)
13927 || (it->mFlags & guestProp::TRANSRESET))
13928 {
13929 fNeedsSaving = true;
13930 break;
13931 }
13932 if (fNeedsSaving)
13933 {
13934 mData->mCurrentStateModified = TRUE;
13935 stsFlags |= SaveSTS_CurStateModified;
13936 }
13937 }
13938#endif
13939
13940 rc = saveStateSettings(stsFlags);
13941
13942 if ( ( oldMachineState != MachineState_PoweredOff
13943 && oldMachineState != MachineState_Aborted
13944 && oldMachineState != MachineState_Teleported
13945 )
13946 && ( aMachineState == MachineState_PoweredOff
13947 || aMachineState == MachineState_Aborted
13948 || aMachineState == MachineState_Teleported
13949 )
13950 )
13951 {
13952 /* we've been shut down for any reason */
13953 /* no special action so far */
13954 }
13955
13956 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13957 LogFlowThisFuncLeave();
13958 return rc;
13959}
13960
13961/**
13962 * Sends the current machine state value to the VM process.
13963 *
13964 * @note Locks this object for reading, then calls a client process.
13965 */
13966HRESULT SessionMachine::updateMachineStateOnClient()
13967{
13968 AutoCaller autoCaller(this);
13969 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13970
13971 ComPtr<IInternalSessionControl> directControl;
13972 {
13973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13974 AssertReturn(!!mData, E_FAIL);
13975 directControl = mData->mSession.mDirectControl;
13976
13977 /* directControl may be already set to NULL here in #OnSessionEnd()
13978 * called too early by the direct session process while there is still
13979 * some operation (like deleting the snapshot) in progress. The client
13980 * process in this case is waiting inside Session::close() for the
13981 * "end session" process object to complete, while #uninit() called by
13982 * #checkForDeath() on the Watcher thread is waiting for the pending
13983 * operation to complete. For now, we accept this inconsistent behavior
13984 * and simply do nothing here. */
13985
13986 if (mData->mSession.mState == SessionState_Unlocking)
13987 return S_OK;
13988
13989 AssertReturn(!directControl.isNull(), E_FAIL);
13990 }
13991
13992 return directControl->UpdateMachineState(mData->mMachineState);
13993}
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