VirtualBox

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

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

Legacy IDE Support: eliminating the API support for configuring the legacy IDE support for AHCI. Legacy IDE fields for settings have been preserved

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 461.9 KB
Line 
1/* $Id: MachineImpl.cpp 43023 2012-08-28 06:43:23Z 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{
294 LogFlowThisFuncEnter();
295 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
296
297 /* Enclose the state transition NotReady->InInit->Ready */
298 AutoInitSpan autoInitSpan(this);
299 AssertReturn(autoInitSpan.isOk(), E_FAIL);
300
301 HRESULT rc = initImpl(aParent, strConfigFile);
302 if (FAILED(rc)) return rc;
303
304 rc = tryCreateMachineConfigFile(fForceOverwrite);
305 if (FAILED(rc)) return rc;
306
307 if (SUCCEEDED(rc))
308 {
309 // create an empty machine config
310 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
311
312 rc = initDataAndChildObjects();
313 }
314
315 if (SUCCEEDED(rc))
316 {
317 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
318 mData->mAccessible = TRUE;
319
320 unconst(mData->mUuid) = aId;
321
322 mUserData->s.strName = strName;
323
324 mUserData->s.llGroups = llGroups;
325
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->id();
339
340 /* Apply BIOS defaults */
341 mBIOSSettings->applyDefaults(aOsType);
342
343 /* Apply network adapters defaults */
344 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
345 mNetworkAdapters[slot]->applyDefaults(aOsType);
346
347 /* Apply serial port defaults */
348 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
349 mSerialPorts[slot]->applyDefaults(aOsType);
350 }
351
352 /* At this point the changing of the current state modification
353 * flag is allowed. */
354 allowStateModification();
355
356 /* commit all changes made during the initialization */
357 commit();
358 }
359
360 /* Confirm a successful initialization when it's the case */
361 if (SUCCEEDED(rc))
362 {
363 if (mData->mAccessible)
364 autoInitSpan.setSucceeded();
365 else
366 autoInitSpan.setLimited();
367 }
368
369 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
370 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
371 mData->mRegistered,
372 mData->mAccessible,
373 rc));
374
375 LogFlowThisFuncLeave();
376
377 return rc;
378}
379
380/**
381 * Initializes a new instance with data from machine XML (formerly Init_Registered).
382 * Gets called in two modes:
383 *
384 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
385 * UUID is specified and we mark the machine as "registered";
386 *
387 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
388 * and the machine remains unregistered until RegisterMachine() is called.
389 *
390 * @param aParent Associated parent object
391 * @param aConfigFile Local file system path to the VM settings file (can
392 * be relative to the VirtualBox config directory).
393 * @param aId UUID of the machine or NULL (see above).
394 *
395 * @return Success indicator. if not S_OK, the machine object is invalid
396 */
397HRESULT Machine::initFromSettings(VirtualBox *aParent,
398 const Utf8Str &strConfigFile,
399 const Guid *aId)
400{
401 LogFlowThisFuncEnter();
402 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
403
404 /* Enclose the state transition NotReady->InInit->Ready */
405 AutoInitSpan autoInitSpan(this);
406 AssertReturn(autoInitSpan.isOk(), E_FAIL);
407
408 HRESULT rc = initImpl(aParent, strConfigFile);
409 if (FAILED(rc)) return rc;
410
411 if (aId)
412 {
413 // loading a registered VM:
414 unconst(mData->mUuid) = *aId;
415 mData->mRegistered = TRUE;
416 // now load the settings from XML:
417 rc = registeredInit();
418 // this calls initDataAndChildObjects() and loadSettings()
419 }
420 else
421 {
422 // opening an unregistered VM (VirtualBox::OpenMachine()):
423 rc = initDataAndChildObjects();
424
425 if (SUCCEEDED(rc))
426 {
427 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
428 mData->mAccessible = TRUE;
429
430 try
431 {
432 // load and parse machine XML; this will throw on XML or logic errors
433 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
434
435 // reject VM UUID duplicates, they can happen if someone
436 // tries to register an already known VM config again
437 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
438 true /* fPermitInaccessible */,
439 false /* aDoSetError */,
440 NULL) != VBOX_E_OBJECT_NOT_FOUND)
441 {
442 throw setError(E_FAIL,
443 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
444 mData->m_strConfigFile.c_str());
445 }
446
447 // use UUID from machine config
448 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
449
450 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
451 NULL /* puuidRegistry */);
452 if (FAILED(rc)) throw rc;
453
454 /* At this point the changing of the current state modification
455 * flag is allowed. */
456 allowStateModification();
457
458 commit();
459 }
460 catch (HRESULT err)
461 {
462 /* we assume that error info is set by the thrower */
463 rc = err;
464 }
465 catch (...)
466 {
467 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
468 }
469 }
470 }
471
472 /* Confirm a successful initialization when it's the case */
473 if (SUCCEEDED(rc))
474 {
475 if (mData->mAccessible)
476 autoInitSpan.setSucceeded();
477 else
478 {
479 autoInitSpan.setLimited();
480
481 // uninit media from this machine's media registry, or else
482 // reloading the settings will fail
483 mParent->unregisterMachineMedia(getId());
484 }
485 }
486
487 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
488 "rc=%08X\n",
489 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
490 mData->mRegistered, mData->mAccessible, rc));
491
492 LogFlowThisFuncLeave();
493
494 return rc;
495}
496
497/**
498 * Initializes a new instance from a machine config that is already in memory
499 * (import OVF case). Since we are importing, the UUID in the machine
500 * config is ignored and we always generate a fresh one.
501 *
502 * @param strName Name for the new machine; this overrides what is specified in config and is used
503 * for the settings file as well.
504 * @param config Machine configuration loaded and parsed from XML.
505 *
506 * @return Success indicator. if not S_OK, the machine object is invalid
507 */
508HRESULT Machine::init(VirtualBox *aParent,
509 const Utf8Str &strName,
510 const settings::MachineConfigFile &config)
511{
512 LogFlowThisFuncEnter();
513
514 /* Enclose the state transition NotReady->InInit->Ready */
515 AutoInitSpan autoInitSpan(this);
516 AssertReturn(autoInitSpan.isOk(), E_FAIL);
517
518 Utf8Str strConfigFile;
519 aParent->getDefaultMachineFolder(strConfigFile);
520 strConfigFile.append(RTPATH_DELIMITER);
521 strConfigFile.append(strName);
522 strConfigFile.append(RTPATH_DELIMITER);
523 strConfigFile.append(strName);
524 strConfigFile.append(".vbox");
525
526 HRESULT rc = initImpl(aParent, strConfigFile);
527 if (FAILED(rc)) return rc;
528
529 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
530 if (FAILED(rc)) return rc;
531
532 rc = initDataAndChildObjects();
533
534 if (SUCCEEDED(rc))
535 {
536 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
537 mData->mAccessible = TRUE;
538
539 // create empty machine config for instance data
540 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
541
542 // generate fresh UUID, ignore machine config
543 unconst(mData->mUuid).create();
544
545 rc = loadMachineDataFromSettings(config,
546 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
547
548 // override VM name as well, it may be different
549 mUserData->s.strName = strName;
550
551 if (SUCCEEDED(rc))
552 {
553 /* At this point the changing of the current state modification
554 * flag is allowed. */
555 allowStateModification();
556
557 /* commit all changes made during the initialization */
558 commit();
559 }
560 }
561
562 /* Confirm a successful initialization when it's the case */
563 if (SUCCEEDED(rc))
564 {
565 if (mData->mAccessible)
566 autoInitSpan.setSucceeded();
567 else
568 {
569 autoInitSpan.setLimited();
570
571 // uninit media from this machine's media registry, or else
572 // reloading the settings will fail
573 mParent->unregisterMachineMedia(getId());
574 }
575 }
576
577 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
578 "rc=%08X\n",
579 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
580 mData->mRegistered, mData->mAccessible, rc));
581
582 LogFlowThisFuncLeave();
583
584 return rc;
585}
586
587/**
588 * Shared code between the various init() implementations.
589 * @param aParent
590 * @return
591 */
592HRESULT Machine::initImpl(VirtualBox *aParent,
593 const Utf8Str &strConfigFile)
594{
595 LogFlowThisFuncEnter();
596
597 AssertReturn(aParent, E_INVALIDARG);
598 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
599
600 HRESULT rc = S_OK;
601
602 /* share the parent weakly */
603 unconst(mParent) = aParent;
604
605 /* allocate the essential machine data structure (the rest will be
606 * allocated later by initDataAndChildObjects() */
607 mData.allocate();
608
609 /* memorize the config file name (as provided) */
610 mData->m_strConfigFile = strConfigFile;
611
612 /* get the full file name */
613 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
614 if (RT_FAILURE(vrc1))
615 return setError(VBOX_E_FILE_ERROR,
616 tr("Invalid machine settings file name '%s' (%Rrc)"),
617 strConfigFile.c_str(),
618 vrc1);
619
620 LogFlowThisFuncLeave();
621
622 return rc;
623}
624
625/**
626 * Tries to create a machine settings file in the path stored in the machine
627 * instance data. Used when a new machine is created to fail gracefully if
628 * the settings file could not be written (e.g. because machine dir is read-only).
629 * @return
630 */
631HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
632{
633 HRESULT rc = S_OK;
634
635 // when we create a new machine, we must be able to create the settings file
636 RTFILE f = NIL_RTFILE;
637 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
638 if ( RT_SUCCESS(vrc)
639 || vrc == VERR_SHARING_VIOLATION
640 )
641 {
642 if (RT_SUCCESS(vrc))
643 RTFileClose(f);
644 if (!fForceOverwrite)
645 rc = setError(VBOX_E_FILE_ERROR,
646 tr("Machine settings file '%s' already exists"),
647 mData->m_strConfigFileFull.c_str());
648 else
649 {
650 /* try to delete the config file, as otherwise the creation
651 * of a new settings file will fail. */
652 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
653 if (RT_FAILURE(vrc2))
654 rc = setError(VBOX_E_FILE_ERROR,
655 tr("Could not delete the existing settings file '%s' (%Rrc)"),
656 mData->m_strConfigFileFull.c_str(), vrc2);
657 }
658 }
659 else if ( vrc != VERR_FILE_NOT_FOUND
660 && vrc != VERR_PATH_NOT_FOUND
661 )
662 rc = setError(VBOX_E_FILE_ERROR,
663 tr("Invalid machine settings file name '%s' (%Rrc)"),
664 mData->m_strConfigFileFull.c_str(),
665 vrc);
666 return rc;
667}
668
669/**
670 * Initializes the registered machine by loading the settings file.
671 * This method is separated from #init() in order to make it possible to
672 * retry the operation after VirtualBox startup instead of refusing to
673 * startup the whole VirtualBox server in case if the settings file of some
674 * registered VM is invalid or inaccessible.
675 *
676 * @note Must be always called from this object's write lock
677 * (unless called from #init() that doesn't need any locking).
678 * @note Locks the mUSBController method for writing.
679 * @note Subclasses must not call this method.
680 */
681HRESULT Machine::registeredInit()
682{
683 AssertReturn(!isSessionMachine(), E_FAIL);
684 AssertReturn(!isSnapshotMachine(), E_FAIL);
685 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
686 AssertReturn(!mData->mAccessible, E_FAIL);
687
688 HRESULT rc = initDataAndChildObjects();
689
690 if (SUCCEEDED(rc))
691 {
692 /* Temporarily reset the registered flag in order to let setters
693 * potentially called from loadSettings() succeed (isMutable() used in
694 * all setters will return FALSE for a Machine instance if mRegistered
695 * is TRUE). */
696 mData->mRegistered = FALSE;
697
698 try
699 {
700 // load and parse machine XML; this will throw on XML or logic errors
701 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
702
703 if (mData->mUuid != mData->pMachineConfigFile->uuid)
704 throw setError(E_FAIL,
705 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
706 mData->pMachineConfigFile->uuid.raw(),
707 mData->m_strConfigFileFull.c_str(),
708 mData->mUuid.toString().c_str(),
709 mParent->settingsFilePath().c_str());
710
711 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
712 NULL /* const Guid *puuidRegistry */);
713 if (FAILED(rc)) throw rc;
714 }
715 catch (HRESULT err)
716 {
717 /* we assume that error info is set by the thrower */
718 rc = err;
719 }
720 catch (...)
721 {
722 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
723 }
724
725 /* Restore the registered flag (even on failure) */
726 mData->mRegistered = TRUE;
727 }
728
729 if (SUCCEEDED(rc))
730 {
731 /* Set mAccessible to TRUE only if we successfully locked and loaded
732 * the settings file */
733 mData->mAccessible = TRUE;
734
735 /* commit all changes made during loading the settings file */
736 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
737 /// @todo r=klaus for some reason the settings loading logic backs up
738 // the settings, and therefore a commit is needed. Should probably be changed.
739 }
740 else
741 {
742 /* If the machine is registered, then, instead of returning a
743 * failure, we mark it as inaccessible and set the result to
744 * success to give it a try later */
745
746 /* fetch the current error info */
747 mData->mAccessError = com::ErrorInfo();
748 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
749 mData->mUuid.raw(),
750 mData->mAccessError.getText().raw()));
751
752 /* rollback all changes */
753 rollback(false /* aNotify */);
754
755 // uninit media from this machine's media registry, or else
756 // reloading the settings will fail
757 mParent->unregisterMachineMedia(getId());
758
759 /* uninitialize the common part to make sure all data is reset to
760 * default (null) values */
761 uninitDataAndChildObjects();
762
763 rc = S_OK;
764 }
765
766 return rc;
767}
768
769/**
770 * Uninitializes the instance.
771 * Called either from FinalRelease() or by the parent when it gets destroyed.
772 *
773 * @note The caller of this method must make sure that this object
774 * a) doesn't have active callers on the current thread and b) is not locked
775 * by the current thread; otherwise uninit() will hang either a) due to
776 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
777 * a dead-lock caused by this thread waiting for all callers on the other
778 * threads are done but preventing them from doing so by holding a lock.
779 */
780void Machine::uninit()
781{
782 LogFlowThisFuncEnter();
783
784 Assert(!isWriteLockOnCurrentThread());
785
786 Assert(!uRegistryNeedsSaving);
787 if (uRegistryNeedsSaving)
788 {
789 AutoCaller autoCaller(this);
790 if (SUCCEEDED(autoCaller.rc()))
791 {
792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
793 saveSettings(NULL, Machine::SaveS_Force);
794 }
795 }
796
797 /* Enclose the state transition Ready->InUninit->NotReady */
798 AutoUninitSpan autoUninitSpan(this);
799 if (autoUninitSpan.uninitDone())
800 return;
801
802 Assert(!isSnapshotMachine());
803 Assert(!isSessionMachine());
804 Assert(!!mData);
805
806 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
807 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
808
809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
810
811 if (!mData->mSession.mMachine.isNull())
812 {
813 /* Theoretically, this can only happen if the VirtualBox server has been
814 * terminated while there were clients running that owned open direct
815 * sessions. Since in this case we are definitely called by
816 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
817 * won't happen on the client watcher thread (because it does
818 * VirtualBox::addCaller() for the duration of the
819 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
820 * cannot happen until the VirtualBox caller is released). This is
821 * important, because SessionMachine::uninit() cannot correctly operate
822 * after we return from this method (it expects the Machine instance is
823 * still valid). We'll call it ourselves below.
824 */
825 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
826 (SessionMachine*)mData->mSession.mMachine));
827
828 if (Global::IsOnlineOrTransient(mData->mMachineState))
829 {
830 LogWarningThisFunc(("Setting state to Aborted!\n"));
831 /* set machine state using SessionMachine reimplementation */
832 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
833 }
834
835 /*
836 * Uninitialize SessionMachine using public uninit() to indicate
837 * an unexpected uninitialization.
838 */
839 mData->mSession.mMachine->uninit();
840 /* SessionMachine::uninit() must set mSession.mMachine to null */
841 Assert(mData->mSession.mMachine.isNull());
842 }
843
844 // uninit media from this machine's media registry, if they're still there
845 Guid uuidMachine(getId());
846
847 /* XXX This will fail with
848 * "cannot be closed because it is still attached to 1 virtual machines"
849 * because at this point we did not call uninitDataAndChildObjects() yet
850 * and therefore also removeBackReference() for all these mediums was not called! */
851 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
852 mParent->unregisterMachineMedia(uuidMachine);
853
854 /* the lock is no more necessary (SessionMachine is uninitialized) */
855 alock.release();
856
857 // has machine been modified?
858 if (mData->flModifications)
859 {
860 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
861 rollback(false /* aNotify */);
862 }
863
864 if (mData->mAccessible)
865 uninitDataAndChildObjects();
866
867 /* free the essential data structure last */
868 mData.free();
869
870 LogFlowThisFuncLeave();
871}
872
873// IMachine properties
874/////////////////////////////////////////////////////////////////////////////
875
876STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
877{
878 CheckComArgOutPointerValid(aParent);
879
880 AutoLimitedCaller autoCaller(this);
881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
882
883 /* mParent is constant during life time, no need to lock */
884 ComObjPtr<VirtualBox> pVirtualBox(mParent);
885 pVirtualBox.queryInterfaceTo(aParent);
886
887 return S_OK;
888}
889
890STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
891{
892 CheckComArgOutPointerValid(aAccessible);
893
894 AutoLimitedCaller autoCaller(this);
895 if (FAILED(autoCaller.rc())) return autoCaller.rc();
896
897 LogFlowThisFunc(("ENTER\n"));
898
899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
900
901 HRESULT rc = S_OK;
902
903 if (!mData->mAccessible)
904 {
905 /* try to initialize the VM once more if not accessible */
906
907 AutoReinitSpan autoReinitSpan(this);
908 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
909
910#ifdef DEBUG
911 LogFlowThisFunc(("Dumping media backreferences\n"));
912 mParent->dumpAllBackRefs();
913#endif
914
915 if (mData->pMachineConfigFile)
916 {
917 // reset the XML file to force loadSettings() (called from registeredInit())
918 // to parse it again; the file might have changed
919 delete mData->pMachineConfigFile;
920 mData->pMachineConfigFile = NULL;
921 }
922
923 rc = registeredInit();
924
925 if (SUCCEEDED(rc) && mData->mAccessible)
926 {
927 autoReinitSpan.setSucceeded();
928
929 /* make sure interesting parties will notice the accessibility
930 * state change */
931 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
932 mParent->onMachineDataChange(mData->mUuid);
933 }
934 }
935
936 if (SUCCEEDED(rc))
937 *aAccessible = mData->mAccessible;
938
939 LogFlowThisFuncLeave();
940
941 return rc;
942}
943
944STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
945{
946 CheckComArgOutPointerValid(aAccessError);
947
948 AutoLimitedCaller autoCaller(this);
949 if (FAILED(autoCaller.rc())) return autoCaller.rc();
950
951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
952
953 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
954 {
955 /* return shortly */
956 aAccessError = NULL;
957 return S_OK;
958 }
959
960 HRESULT rc = S_OK;
961
962 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
963 rc = errorInfo.createObject();
964 if (SUCCEEDED(rc))
965 {
966 errorInfo->init(mData->mAccessError.getResultCode(),
967 mData->mAccessError.getInterfaceID().ref(),
968 Utf8Str(mData->mAccessError.getComponent()).c_str(),
969 Utf8Str(mData->mAccessError.getText()));
970 rc = errorInfo.queryInterfaceTo(aAccessError);
971 }
972
973 return rc;
974}
975
976STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
977{
978 CheckComArgOutPointerValid(aName);
979
980 AutoCaller autoCaller(this);
981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
982
983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
984
985 mUserData->s.strName.cloneTo(aName);
986
987 return S_OK;
988}
989
990STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
991{
992 CheckComArgStrNotEmptyOrNull(aName);
993
994 AutoCaller autoCaller(this);
995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
996
997 // prohibit setting a UUID only as the machine name, or else it can
998 // never be found by findMachine()
999 Guid test(aName);
1000 if (test.isNotEmpty())
1001 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1002
1003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1004
1005 HRESULT rc = checkStateDependency(MutableStateDep);
1006 if (FAILED(rc)) return rc;
1007
1008 setModified(IsModified_MachineData);
1009 mUserData.backup();
1010 mUserData->s.strName = aName;
1011
1012 return S_OK;
1013}
1014
1015STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1016{
1017 CheckComArgOutPointerValid(aDescription);
1018
1019 AutoCaller autoCaller(this);
1020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1021
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 mUserData->s.strDescription.cloneTo(aDescription);
1025
1026 return S_OK;
1027}
1028
1029STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1030{
1031 AutoCaller autoCaller(this);
1032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1033
1034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 // this can be done in principle in any state as it doesn't affect the VM
1037 // significantly, but play safe by not messing around while complex
1038 // activities are going on
1039 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1040 if (FAILED(rc)) return rc;
1041
1042 setModified(IsModified_MachineData);
1043 mUserData.backup();
1044 mUserData->s.strDescription = aDescription;
1045
1046 return S_OK;
1047}
1048
1049STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1050{
1051 CheckComArgOutPointerValid(aId);
1052
1053 AutoLimitedCaller autoCaller(this);
1054 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1055
1056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1057
1058 mData->mUuid.toUtf16().cloneTo(aId);
1059
1060 return S_OK;
1061}
1062
1063STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1064{
1065 CheckComArgOutSafeArrayPointerValid(aGroups);
1066
1067 AutoCaller autoCaller(this);
1068 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1069
1070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1071 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1072 size_t i = 0;
1073 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1074 it != mUserData->s.llGroups.end();
1075 ++it, i++)
1076 {
1077 Bstr tmp = *it;
1078 tmp.cloneTo(&groups[i]);
1079 }
1080 groups.detachTo(ComSafeArrayOutArg(aGroups));
1081
1082 return S_OK;
1083}
1084
1085STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1086{
1087 AutoCaller autoCaller(this);
1088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1089
1090 StringsList llGroups;
1091 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1092 if (FAILED(rc))
1093 return rc;
1094
1095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1096
1097 // changing machine groups is possible while the VM is offline
1098 rc = checkStateDependency(OfflineStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.llGroups = llGroups;
1104
1105 return S_OK;
1106}
1107
1108STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1109{
1110 CheckComArgOutPointerValid(aOSTypeId);
1111
1112 AutoCaller autoCaller(this);
1113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1114
1115 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1116
1117 mUserData->s.strOsType.cloneTo(aOSTypeId);
1118
1119 return S_OK;
1120}
1121
1122STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1123{
1124 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1125
1126 AutoCaller autoCaller(this);
1127 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1128
1129 /* look up the object by Id to check it is valid */
1130 ComPtr<IGuestOSType> guestOSType;
1131 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1132 if (FAILED(rc)) return rc;
1133
1134 /* when setting, always use the "etalon" value for consistency -- lookup
1135 * by ID is case-insensitive and the input value may have different case */
1136 Bstr osTypeId;
1137 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1138 if (FAILED(rc)) return rc;
1139
1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 rc = checkStateDependency(MutableStateDep);
1143 if (FAILED(rc)) return rc;
1144
1145 setModified(IsModified_MachineData);
1146 mUserData.backup();
1147 mUserData->s.strOsType = osTypeId;
1148
1149 return S_OK;
1150}
1151
1152
1153STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1154{
1155 CheckComArgOutPointerValid(aFirmwareType);
1156
1157 AutoCaller autoCaller(this);
1158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1159
1160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1161
1162 *aFirmwareType = mHWData->mFirmwareType;
1163
1164 return S_OK;
1165}
1166
1167STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1168{
1169 AutoCaller autoCaller(this);
1170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 HRESULT rc = checkStateDependency(MutableStateDep);
1174 if (FAILED(rc)) return rc;
1175
1176 setModified(IsModified_MachineData);
1177 mHWData.backup();
1178 mHWData->mFirmwareType = aFirmwareType;
1179
1180 return S_OK;
1181}
1182
1183STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1184{
1185 CheckComArgOutPointerValid(aKeyboardHIDType);
1186
1187 AutoCaller autoCaller(this);
1188 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1189
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1193
1194 return S_OK;
1195}
1196
1197STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1198{
1199 AutoCaller autoCaller(this);
1200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 HRESULT rc = checkStateDependency(MutableStateDep);
1204 if (FAILED(rc)) return rc;
1205
1206 setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1209
1210 return S_OK;
1211}
1212
1213STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1214{
1215 CheckComArgOutPointerValid(aPointingHIDType);
1216
1217 AutoCaller autoCaller(this);
1218 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1219
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 *aPointingHIDType = mHWData->mPointingHIDType;
1223
1224 return S_OK;
1225}
1226
1227STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1228{
1229 AutoCaller autoCaller(this);
1230 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 HRESULT rc = checkStateDependency(MutableStateDep);
1234 if (FAILED(rc)) return rc;
1235
1236 setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mPointingHIDType = aPointingHIDType;
1239
1240 return S_OK;
1241}
1242
1243STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1244{
1245 CheckComArgOutPointerValid(aChipsetType);
1246
1247 AutoCaller autoCaller(this);
1248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1249
1250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 *aChipsetType = mHWData->mChipsetType;
1253
1254 return S_OK;
1255}
1256
1257STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1258{
1259 AutoCaller autoCaller(this);
1260 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 HRESULT rc = checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 if (aChipsetType != mHWData->mChipsetType)
1267 {
1268 setModified(IsModified_MachineData);
1269 mHWData.backup();
1270 mHWData->mChipsetType = aChipsetType;
1271
1272 // Resize network adapter array, to be finalized on commit/rollback.
1273 // We must not throw away entries yet, otherwise settings are lost
1274 // without a way to roll back.
1275 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1276 uint32_t oldCount = mNetworkAdapters.size();
1277 if (newCount > oldCount)
1278 {
1279 mNetworkAdapters.resize(newCount);
1280 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1281 {
1282 unconst(mNetworkAdapters[slot]).createObject();
1283 mNetworkAdapters[slot]->init(this, slot);
1284 }
1285 }
1286 }
1287
1288 return S_OK;
1289}
1290
1291STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1292{
1293 CheckComArgOutPointerValid(aHWVersion);
1294
1295 AutoCaller autoCaller(this);
1296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1297
1298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1299
1300 mHWData->mHWVersion.cloneTo(aHWVersion);
1301
1302 return S_OK;
1303}
1304
1305STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1306{
1307 /* check known version */
1308 Utf8Str hwVersion = aHWVersion;
1309 if ( hwVersion.compare("1") != 0
1310 && hwVersion.compare("2") != 0)
1311 return setError(E_INVALIDARG,
1312 tr("Invalid hardware version: %ls\n"), aHWVersion);
1313
1314 AutoCaller autoCaller(this);
1315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1316
1317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1318
1319 HRESULT rc = checkStateDependency(MutableStateDep);
1320 if (FAILED(rc)) return rc;
1321
1322 setModified(IsModified_MachineData);
1323 mHWData.backup();
1324 mHWData->mHWVersion = hwVersion;
1325
1326 return S_OK;
1327}
1328
1329STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1330{
1331 CheckComArgOutPointerValid(aUUID);
1332
1333 AutoCaller autoCaller(this);
1334 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1335
1336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1337
1338 if (!mHWData->mHardwareUUID.isEmpty())
1339 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1340 else
1341 mData->mUuid.toUtf16().cloneTo(aUUID);
1342
1343 return S_OK;
1344}
1345
1346STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1347{
1348 Guid hardwareUUID(aUUID);
1349 if (hardwareUUID.isEmpty())
1350 return E_INVALIDARG;
1351
1352 AutoCaller autoCaller(this);
1353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1354
1355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1356
1357 HRESULT rc = checkStateDependency(MutableStateDep);
1358 if (FAILED(rc)) return rc;
1359
1360 setModified(IsModified_MachineData);
1361 mHWData.backup();
1362 if (hardwareUUID == mData->mUuid)
1363 mHWData->mHardwareUUID.clear();
1364 else
1365 mHWData->mHardwareUUID = hardwareUUID;
1366
1367 return S_OK;
1368}
1369
1370STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1371{
1372 CheckComArgOutPointerValid(memorySize);
1373
1374 AutoCaller autoCaller(this);
1375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1376
1377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1378
1379 *memorySize = mHWData->mMemorySize;
1380
1381 return S_OK;
1382}
1383
1384STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1385{
1386 /* check RAM limits */
1387 if ( memorySize < MM_RAM_MIN_IN_MB
1388 || memorySize > MM_RAM_MAX_IN_MB
1389 )
1390 return setError(E_INVALIDARG,
1391 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1392 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1393
1394 AutoCaller autoCaller(this);
1395 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1396
1397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 HRESULT rc = checkStateDependency(MutableStateDep);
1400 if (FAILED(rc)) return rc;
1401
1402 setModified(IsModified_MachineData);
1403 mHWData.backup();
1404 mHWData->mMemorySize = memorySize;
1405
1406 return S_OK;
1407}
1408
1409STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1410{
1411 CheckComArgOutPointerValid(CPUCount);
1412
1413 AutoCaller autoCaller(this);
1414 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1415
1416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1417
1418 *CPUCount = mHWData->mCPUCount;
1419
1420 return S_OK;
1421}
1422
1423STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1424{
1425 /* check CPU limits */
1426 if ( CPUCount < SchemaDefs::MinCPUCount
1427 || CPUCount > SchemaDefs::MaxCPUCount
1428 )
1429 return setError(E_INVALIDARG,
1430 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1431 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1432
1433 AutoCaller autoCaller(this);
1434 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1435
1436 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1437
1438 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1439 if (mHWData->mCPUHotPlugEnabled)
1440 {
1441 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1442 {
1443 if (mHWData->mCPUAttached[idx])
1444 return setError(E_INVALIDARG,
1445 tr("There is still a CPU attached to socket %lu."
1446 "Detach the CPU before removing the socket"),
1447 CPUCount, idx+1);
1448 }
1449 }
1450
1451 HRESULT rc = checkStateDependency(MutableStateDep);
1452 if (FAILED(rc)) return rc;
1453
1454 setModified(IsModified_MachineData);
1455 mHWData.backup();
1456 mHWData->mCPUCount = CPUCount;
1457
1458 return S_OK;
1459}
1460
1461STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1462{
1463 CheckComArgOutPointerValid(aExecutionCap);
1464
1465 AutoCaller autoCaller(this);
1466 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1467
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 *aExecutionCap = mHWData->mCpuExecutionCap;
1471
1472 return S_OK;
1473}
1474
1475STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1476{
1477 HRESULT rc = S_OK;
1478
1479 /* check throttle limits */
1480 if ( aExecutionCap < 1
1481 || aExecutionCap > 100
1482 )
1483 return setError(E_INVALIDARG,
1484 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1485 aExecutionCap, 1, 100);
1486
1487 AutoCaller autoCaller(this);
1488 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1489
1490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1491
1492 alock.release();
1493 rc = onCPUExecutionCapChange(aExecutionCap);
1494 alock.acquire();
1495 if (FAILED(rc)) return rc;
1496
1497 setModified(IsModified_MachineData);
1498 mHWData.backup();
1499 mHWData->mCpuExecutionCap = aExecutionCap;
1500
1501 /* Save settings if online - todo why is this required?? */
1502 if (Global::IsOnline(mData->mMachineState))
1503 saveSettings(NULL);
1504
1505 return S_OK;
1506}
1507
1508
1509STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1510{
1511 CheckComArgOutPointerValid(enabled);
1512
1513 AutoCaller autoCaller(this);
1514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1515
1516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1517
1518 *enabled = mHWData->mCPUHotPlugEnabled;
1519
1520 return S_OK;
1521}
1522
1523STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1524{
1525 HRESULT rc = S_OK;
1526
1527 AutoCaller autoCaller(this);
1528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1529
1530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 rc = checkStateDependency(MutableStateDep);
1533 if (FAILED(rc)) return rc;
1534
1535 if (mHWData->mCPUHotPlugEnabled != enabled)
1536 {
1537 if (enabled)
1538 {
1539 setModified(IsModified_MachineData);
1540 mHWData.backup();
1541
1542 /* Add the amount of CPUs currently attached */
1543 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1544 {
1545 mHWData->mCPUAttached[i] = true;
1546 }
1547 }
1548 else
1549 {
1550 /*
1551 * We can disable hotplug only if the amount of maximum CPUs is equal
1552 * to the amount of attached CPUs
1553 */
1554 unsigned cCpusAttached = 0;
1555 unsigned iHighestId = 0;
1556
1557 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1558 {
1559 if (mHWData->mCPUAttached[i])
1560 {
1561 cCpusAttached++;
1562 iHighestId = i;
1563 }
1564 }
1565
1566 if ( (cCpusAttached != mHWData->mCPUCount)
1567 || (iHighestId >= mHWData->mCPUCount))
1568 return setError(E_INVALIDARG,
1569 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1570
1571 setModified(IsModified_MachineData);
1572 mHWData.backup();
1573 }
1574 }
1575
1576 mHWData->mCPUHotPlugEnabled = enabled;
1577
1578 return rc;
1579}
1580
1581STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1582{
1583#ifdef VBOX_WITH_USB_CARDREADER
1584 CheckComArgOutPointerValid(enabled);
1585
1586 AutoCaller autoCaller(this);
1587 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1588
1589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1590
1591 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1592
1593 return S_OK;
1594#else
1595 NOREF(enabled);
1596 return E_NOTIMPL;
1597#endif
1598}
1599
1600STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1601{
1602#ifdef VBOX_WITH_USB_CARDREADER
1603 AutoCaller autoCaller(this);
1604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1606
1607 HRESULT rc = checkStateDependency(MutableStateDep);
1608 if (FAILED(rc)) return rc;
1609
1610 setModified(IsModified_MachineData);
1611 mHWData.backup();
1612 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1613
1614 return S_OK;
1615#else
1616 NOREF(enabled);
1617 return E_NOTIMPL;
1618#endif
1619}
1620
1621STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1622{
1623 NOREF(enabled);
1624 return E_NOTIMPL;
1625}
1626
1627STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1628{
1629 NOREF(enabled);
1630 return E_NOTIMPL;
1631}
1632
1633STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1634{
1635 CheckComArgOutPointerValid(enabled);
1636
1637 AutoCaller autoCaller(this);
1638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640
1641 *enabled = mHWData->mHPETEnabled;
1642
1643 return S_OK;
1644}
1645
1646STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1647{
1648 HRESULT rc = S_OK;
1649
1650 AutoCaller autoCaller(this);
1651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1653
1654 rc = checkStateDependency(MutableStateDep);
1655 if (FAILED(rc)) return rc;
1656
1657 setModified(IsModified_MachineData);
1658 mHWData.backup();
1659
1660 mHWData->mHPETEnabled = enabled;
1661
1662 return rc;
1663}
1664
1665STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled)
1666{
1667 AutoCaller autoCaller(this);
1668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1669
1670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1671
1672 *fEnabled = mHWData->mVideoCaptureEnabled;
1673 return S_OK;
1674}
1675
1676STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1677{
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682 mHWData->mVideoCaptureEnabled = fEnabled;
1683 return S_OK;
1684}
1685
1686STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1687{
1688 AutoCaller autoCaller(this);
1689 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1690
1691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1692 mHWData->mVideoCaptureFile.cloneTo(apFile);
1693 return S_OK;
1694}
1695
1696STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1697{
1698 Utf8Str strFile(aFile);
1699 AutoCaller autoCaller(this);
1700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1701
1702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1703 if(strFile.isEmpty())
1704 strFile = "VideoCap.webm";
1705 mHWData->mVideoCaptureFile = strFile;
1706 return S_OK;
1707}
1708
1709
1710STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1711{
1712 AutoCaller autoCaller(this);
1713 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1714
1715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1716 *ulHorzRes = mHWData->mVideoCaptureWidth;
1717 return S_OK;
1718}
1719
1720STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1721{
1722 AutoCaller autoCaller(this);
1723 if (FAILED(autoCaller.rc()))
1724 {
1725 LogFlow(("Autolocked failed\n"));
1726 return autoCaller.rc();
1727 }
1728
1729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1730 mHWData->mVideoCaptureWidth = ulHorzRes;
1731 return S_OK;
1732}
1733
1734STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1735{
1736 AutoCaller autoCaller(this);
1737 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1738
1739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1740 *ulVertRes = mHWData->mVideoCaptureHeight;
1741 return S_OK;
1742}
1743
1744STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1745{
1746 AutoCaller autoCaller(this);
1747 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1748
1749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1750 mHWData->mVideoCaptureHeight = ulVertRes;
1751 return S_OK;
1752}
1753
1754STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1755{
1756 CheckComArgOutPointerValid(memorySize);
1757
1758 AutoCaller autoCaller(this);
1759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1760
1761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1762
1763 *memorySize = mHWData->mVRAMSize;
1764
1765 return S_OK;
1766}
1767
1768STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1769{
1770 /* check VRAM limits */
1771 if (memorySize < SchemaDefs::MinGuestVRAM ||
1772 memorySize > SchemaDefs::MaxGuestVRAM)
1773 return setError(E_INVALIDARG,
1774 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1775 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1776
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1781
1782 HRESULT rc = checkStateDependency(MutableStateDep);
1783 if (FAILED(rc)) return rc;
1784
1785 setModified(IsModified_MachineData);
1786 mHWData.backup();
1787 mHWData->mVRAMSize = memorySize;
1788
1789 return S_OK;
1790}
1791
1792/** @todo this method should not be public */
1793STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1794{
1795 CheckComArgOutPointerValid(memoryBalloonSize);
1796
1797 AutoCaller autoCaller(this);
1798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1799
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1803
1804 return S_OK;
1805}
1806
1807/**
1808 * Set the memory balloon size.
1809 *
1810 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1811 * we have to make sure that we never call IGuest from here.
1812 */
1813STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1814{
1815 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1816#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1817 /* check limits */
1818 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1819 return setError(E_INVALIDARG,
1820 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1821 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1822
1823 AutoCaller autoCaller(this);
1824 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1825
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 setModified(IsModified_MachineData);
1829 mHWData.backup();
1830 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1831
1832 return S_OK;
1833#else
1834 NOREF(memoryBalloonSize);
1835 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1836#endif
1837}
1838
1839STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1840{
1841 CheckComArgOutPointerValid(enabled);
1842
1843 AutoCaller autoCaller(this);
1844 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1845
1846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 *enabled = mHWData->mPageFusionEnabled;
1849 return S_OK;
1850}
1851
1852STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1853{
1854#ifdef VBOX_WITH_PAGE_SHARING
1855 AutoCaller autoCaller(this);
1856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1857
1858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1859
1860 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1861 setModified(IsModified_MachineData);
1862 mHWData.backup();
1863 mHWData->mPageFusionEnabled = enabled;
1864 return S_OK;
1865#else
1866 NOREF(enabled);
1867 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1868#endif
1869}
1870
1871STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1872{
1873 CheckComArgOutPointerValid(enabled);
1874
1875 AutoCaller autoCaller(this);
1876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1877
1878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 *enabled = mHWData->mAccelerate3DEnabled;
1881
1882 return S_OK;
1883}
1884
1885STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1886{
1887 AutoCaller autoCaller(this);
1888 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1889
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 HRESULT rc = checkStateDependency(MutableStateDep);
1893 if (FAILED(rc)) return rc;
1894
1895 /** @todo check validity! */
1896
1897 setModified(IsModified_MachineData);
1898 mHWData.backup();
1899 mHWData->mAccelerate3DEnabled = enable;
1900
1901 return S_OK;
1902}
1903
1904
1905STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1906{
1907 CheckComArgOutPointerValid(enabled);
1908
1909 AutoCaller autoCaller(this);
1910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1911
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 *enabled = mHWData->mAccelerate2DVideoEnabled;
1915
1916 return S_OK;
1917}
1918
1919STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1920{
1921 AutoCaller autoCaller(this);
1922 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1923
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 HRESULT rc = checkStateDependency(MutableStateDep);
1927 if (FAILED(rc)) return rc;
1928
1929 /** @todo check validity! */
1930
1931 setModified(IsModified_MachineData);
1932 mHWData.backup();
1933 mHWData->mAccelerate2DVideoEnabled = enable;
1934
1935 return S_OK;
1936}
1937
1938STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1939{
1940 CheckComArgOutPointerValid(monitorCount);
1941
1942 AutoCaller autoCaller(this);
1943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1944
1945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1946
1947 *monitorCount = mHWData->mMonitorCount;
1948
1949 return S_OK;
1950}
1951
1952STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1953{
1954 /* make sure monitor count is a sensible number */
1955 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1956 return setError(E_INVALIDARG,
1957 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1958 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1959
1960 AutoCaller autoCaller(this);
1961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1962
1963 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1964
1965 HRESULT rc = checkStateDependency(MutableStateDep);
1966 if (FAILED(rc)) return rc;
1967
1968 setModified(IsModified_MachineData);
1969 mHWData.backup();
1970 mHWData->mMonitorCount = monitorCount;
1971
1972 return S_OK;
1973}
1974
1975STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1976{
1977 CheckComArgOutPointerValid(biosSettings);
1978
1979 AutoCaller autoCaller(this);
1980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1981
1982 /* mBIOSSettings is constant during life time, no need to lock */
1983 mBIOSSettings.queryInterfaceTo(biosSettings);
1984
1985 return S_OK;
1986}
1987
1988STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1989{
1990 CheckComArgOutPointerValid(aVal);
1991
1992 AutoCaller autoCaller(this);
1993 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1994
1995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1996
1997 switch(property)
1998 {
1999 case CPUPropertyType_PAE:
2000 *aVal = mHWData->mPAEEnabled;
2001 break;
2002
2003 case CPUPropertyType_Synthetic:
2004 *aVal = mHWData->mSyntheticCpu;
2005 break;
2006
2007 default:
2008 return E_INVALIDARG;
2009 }
2010 return S_OK;
2011}
2012
2013STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2014{
2015 AutoCaller autoCaller(this);
2016 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2017
2018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 HRESULT rc = checkStateDependency(MutableStateDep);
2021 if (FAILED(rc)) return rc;
2022
2023 switch(property)
2024 {
2025 case CPUPropertyType_PAE:
2026 setModified(IsModified_MachineData);
2027 mHWData.backup();
2028 mHWData->mPAEEnabled = !!aVal;
2029 break;
2030
2031 case CPUPropertyType_Synthetic:
2032 setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mSyntheticCpu = !!aVal;
2035 break;
2036
2037 default:
2038 return E_INVALIDARG;
2039 }
2040 return S_OK;
2041}
2042
2043STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2044{
2045 CheckComArgOutPointerValid(aValEax);
2046 CheckComArgOutPointerValid(aValEbx);
2047 CheckComArgOutPointerValid(aValEcx);
2048 CheckComArgOutPointerValid(aValEdx);
2049
2050 AutoCaller autoCaller(this);
2051 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2052
2053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2054
2055 switch(aId)
2056 {
2057 case 0x0:
2058 case 0x1:
2059 case 0x2:
2060 case 0x3:
2061 case 0x4:
2062 case 0x5:
2063 case 0x6:
2064 case 0x7:
2065 case 0x8:
2066 case 0x9:
2067 case 0xA:
2068 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2069 return E_INVALIDARG;
2070
2071 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2072 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2073 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2074 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2075 break;
2076
2077 case 0x80000000:
2078 case 0x80000001:
2079 case 0x80000002:
2080 case 0x80000003:
2081 case 0x80000004:
2082 case 0x80000005:
2083 case 0x80000006:
2084 case 0x80000007:
2085 case 0x80000008:
2086 case 0x80000009:
2087 case 0x8000000A:
2088 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2089 return E_INVALIDARG;
2090
2091 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2092 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2093 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2094 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2095 break;
2096
2097 default:
2098 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2099 }
2100 return S_OK;
2101}
2102
2103STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2104{
2105 AutoCaller autoCaller(this);
2106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2107
2108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2109
2110 HRESULT rc = checkStateDependency(MutableStateDep);
2111 if (FAILED(rc)) return rc;
2112
2113 switch(aId)
2114 {
2115 case 0x0:
2116 case 0x1:
2117 case 0x2:
2118 case 0x3:
2119 case 0x4:
2120 case 0x5:
2121 case 0x6:
2122 case 0x7:
2123 case 0x8:
2124 case 0x9:
2125 case 0xA:
2126 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2127 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2128 setModified(IsModified_MachineData);
2129 mHWData.backup();
2130 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2131 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2132 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2133 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2134 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2135 break;
2136
2137 case 0x80000000:
2138 case 0x80000001:
2139 case 0x80000002:
2140 case 0x80000003:
2141 case 0x80000004:
2142 case 0x80000005:
2143 case 0x80000006:
2144 case 0x80000007:
2145 case 0x80000008:
2146 case 0x80000009:
2147 case 0x8000000A:
2148 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2149 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2150 setModified(IsModified_MachineData);
2151 mHWData.backup();
2152 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2153 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2154 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2155 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2157 break;
2158
2159 default:
2160 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2161 }
2162 return S_OK;
2163}
2164
2165STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2166{
2167 AutoCaller autoCaller(this);
2168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2169
2170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2171
2172 HRESULT rc = checkStateDependency(MutableStateDep);
2173 if (FAILED(rc)) return rc;
2174
2175 switch(aId)
2176 {
2177 case 0x0:
2178 case 0x1:
2179 case 0x2:
2180 case 0x3:
2181 case 0x4:
2182 case 0x5:
2183 case 0x6:
2184 case 0x7:
2185 case 0x8:
2186 case 0x9:
2187 case 0xA:
2188 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2189 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2190 setModified(IsModified_MachineData);
2191 mHWData.backup();
2192 /* Invalidate leaf. */
2193 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2194 break;
2195
2196 case 0x80000000:
2197 case 0x80000001:
2198 case 0x80000002:
2199 case 0x80000003:
2200 case 0x80000004:
2201 case 0x80000005:
2202 case 0x80000006:
2203 case 0x80000007:
2204 case 0x80000008:
2205 case 0x80000009:
2206 case 0x8000000A:
2207 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2208 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2209 setModified(IsModified_MachineData);
2210 mHWData.backup();
2211 /* Invalidate leaf. */
2212 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2213 break;
2214
2215 default:
2216 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2217 }
2218 return S_OK;
2219}
2220
2221STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2222{
2223 AutoCaller autoCaller(this);
2224 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2225
2226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2227
2228 HRESULT rc = checkStateDependency(MutableStateDep);
2229 if (FAILED(rc)) return rc;
2230
2231 setModified(IsModified_MachineData);
2232 mHWData.backup();
2233
2234 /* Invalidate all standard leafs. */
2235 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2236 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2237
2238 /* Invalidate all extended leafs. */
2239 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2240 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2241
2242 return S_OK;
2243}
2244
2245STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2246{
2247 CheckComArgOutPointerValid(aVal);
2248
2249 AutoCaller autoCaller(this);
2250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2251
2252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2253
2254 switch(property)
2255 {
2256 case HWVirtExPropertyType_Enabled:
2257 *aVal = mHWData->mHWVirtExEnabled;
2258 break;
2259
2260 case HWVirtExPropertyType_Exclusive:
2261 *aVal = mHWData->mHWVirtExExclusive;
2262 break;
2263
2264 case HWVirtExPropertyType_VPID:
2265 *aVal = mHWData->mHWVirtExVPIDEnabled;
2266 break;
2267
2268 case HWVirtExPropertyType_NestedPaging:
2269 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2270 break;
2271
2272 case HWVirtExPropertyType_LargePages:
2273 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2274#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2275 *aVal = FALSE;
2276#endif
2277 break;
2278
2279 case HWVirtExPropertyType_Force:
2280 *aVal = mHWData->mHWVirtExForceEnabled;
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2290{
2291 AutoCaller autoCaller(this);
2292 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2293
2294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2295
2296 HRESULT rc = checkStateDependency(MutableStateDep);
2297 if (FAILED(rc)) return rc;
2298
2299 switch(property)
2300 {
2301 case HWVirtExPropertyType_Enabled:
2302 setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mHWVirtExEnabled = !!aVal;
2305 break;
2306
2307 case HWVirtExPropertyType_Exclusive:
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mHWVirtExExclusive = !!aVal;
2311 break;
2312
2313 case HWVirtExPropertyType_VPID:
2314 setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2317 break;
2318
2319 case HWVirtExPropertyType_NestedPaging:
2320 setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2323 break;
2324
2325 case HWVirtExPropertyType_LargePages:
2326 setModified(IsModified_MachineData);
2327 mHWData.backup();
2328 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2329 break;
2330
2331 case HWVirtExPropertyType_Force:
2332 setModified(IsModified_MachineData);
2333 mHWData.backup();
2334 mHWData->mHWVirtExForceEnabled = !!aVal;
2335 break;
2336
2337 default:
2338 return E_INVALIDARG;
2339 }
2340
2341 return S_OK;
2342}
2343
2344STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2345{
2346 CheckComArgOutPointerValid(aSnapshotFolder);
2347
2348 AutoCaller autoCaller(this);
2349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2350
2351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2352
2353 Utf8Str strFullSnapshotFolder;
2354 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2355 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2356
2357 return S_OK;
2358}
2359
2360STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2361{
2362 /* @todo (r=dmik):
2363 * 1. Allow to change the name of the snapshot folder containing snapshots
2364 * 2. Rename the folder on disk instead of just changing the property
2365 * value (to be smart and not to leave garbage). Note that it cannot be
2366 * done here because the change may be rolled back. Thus, the right
2367 * place is #saveSettings().
2368 */
2369
2370 AutoCaller autoCaller(this);
2371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2372
2373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2374
2375 HRESULT rc = checkStateDependency(MutableStateDep);
2376 if (FAILED(rc)) return rc;
2377
2378 if (!mData->mCurrentSnapshot.isNull())
2379 return setError(E_FAIL,
2380 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2381
2382 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2383
2384 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2385 if (strSnapshotFolder.isEmpty())
2386 strSnapshotFolder = "Snapshots";
2387 int vrc = calculateFullPath(strSnapshotFolder,
2388 strSnapshotFolder);
2389 if (RT_FAILURE(vrc))
2390 return setError(E_FAIL,
2391 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2392 aSnapshotFolder, vrc);
2393
2394 setModified(IsModified_MachineData);
2395 mUserData.backup();
2396
2397 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2398
2399 return S_OK;
2400}
2401
2402STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2403{
2404 CheckComArgOutSafeArrayPointerValid(aAttachments);
2405
2406 AutoCaller autoCaller(this);
2407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2408
2409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2410
2411 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2412 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2413
2414 return S_OK;
2415}
2416
2417STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2418{
2419 CheckComArgOutPointerValid(vrdeServer);
2420
2421 AutoCaller autoCaller(this);
2422 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2423
2424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2425
2426 Assert(!!mVRDEServer);
2427 mVRDEServer.queryInterfaceTo(vrdeServer);
2428
2429 return S_OK;
2430}
2431
2432STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2433{
2434 CheckComArgOutPointerValid(audioAdapter);
2435
2436 AutoCaller autoCaller(this);
2437 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2438
2439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2440
2441 mAudioAdapter.queryInterfaceTo(audioAdapter);
2442 return S_OK;
2443}
2444
2445STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2446{
2447#ifdef VBOX_WITH_VUSB
2448 CheckComArgOutPointerValid(aUSBController);
2449
2450 AutoCaller autoCaller(this);
2451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2452
2453 clearError();
2454 MultiResult rc(S_OK);
2455
2456# ifdef VBOX_WITH_USB
2457 rc = mParent->host()->checkUSBProxyService();
2458 if (FAILED(rc)) return rc;
2459# endif
2460
2461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2462
2463 return rc = mUSBController.queryInterfaceTo(aUSBController);
2464#else
2465 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2466 * extended error info to indicate that USB is simply not available
2467 * (w/o treating it as a failure), for example, as in OSE */
2468 NOREF(aUSBController);
2469 ReturnComNotImplemented();
2470#endif /* VBOX_WITH_VUSB */
2471}
2472
2473STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2474{
2475 CheckComArgOutPointerValid(aFilePath);
2476
2477 AutoLimitedCaller autoCaller(this);
2478 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2479
2480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2481
2482 mData->m_strConfigFileFull.cloneTo(aFilePath);
2483 return S_OK;
2484}
2485
2486STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2487{
2488 CheckComArgOutPointerValid(aModified);
2489
2490 AutoCaller autoCaller(this);
2491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2492
2493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2494
2495 HRESULT rc = checkStateDependency(MutableStateDep);
2496 if (FAILED(rc)) return rc;
2497
2498 if (!mData->pMachineConfigFile->fileExists())
2499 // this is a new machine, and no config file exists yet:
2500 *aModified = TRUE;
2501 else
2502 *aModified = (mData->flModifications != 0);
2503
2504 return S_OK;
2505}
2506
2507STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2508{
2509 CheckComArgOutPointerValid(aSessionState);
2510
2511 AutoCaller autoCaller(this);
2512 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2513
2514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2515
2516 *aSessionState = mData->mSession.mState;
2517
2518 return S_OK;
2519}
2520
2521STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2522{
2523 CheckComArgOutPointerValid(aSessionType);
2524
2525 AutoCaller autoCaller(this);
2526 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2527
2528 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2529
2530 mData->mSession.mType.cloneTo(aSessionType);
2531
2532 return S_OK;
2533}
2534
2535STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2536{
2537 CheckComArgOutPointerValid(aSessionPID);
2538
2539 AutoCaller autoCaller(this);
2540 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2541
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 *aSessionPID = mData->mSession.mPID;
2545
2546 return S_OK;
2547}
2548
2549STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2550{
2551 CheckComArgOutPointerValid(machineState);
2552
2553 AutoCaller autoCaller(this);
2554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2555
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 *machineState = mData->mMachineState;
2559
2560 return S_OK;
2561}
2562
2563STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2564{
2565 CheckComArgOutPointerValid(aLastStateChange);
2566
2567 AutoCaller autoCaller(this);
2568 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2569
2570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2571
2572 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2573
2574 return S_OK;
2575}
2576
2577STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2578{
2579 CheckComArgOutPointerValid(aStateFilePath);
2580
2581 AutoCaller autoCaller(this);
2582 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2583
2584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2585
2586 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2587
2588 return S_OK;
2589}
2590
2591STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2592{
2593 CheckComArgOutPointerValid(aLogFolder);
2594
2595 AutoCaller autoCaller(this);
2596 AssertComRCReturnRC(autoCaller.rc());
2597
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 Utf8Str logFolder;
2601 getLogFolder(logFolder);
2602 logFolder.cloneTo(aLogFolder);
2603
2604 return S_OK;
2605}
2606
2607STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2608{
2609 CheckComArgOutPointerValid(aCurrentSnapshot);
2610
2611 AutoCaller autoCaller(this);
2612 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2613
2614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2617
2618 return S_OK;
2619}
2620
2621STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2622{
2623 CheckComArgOutPointerValid(aSnapshotCount);
2624
2625 AutoCaller autoCaller(this);
2626 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2627
2628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2631 ? 0
2632 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2633
2634 return S_OK;
2635}
2636
2637STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2638{
2639 CheckComArgOutPointerValid(aCurrentStateModified);
2640
2641 AutoCaller autoCaller(this);
2642 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2643
2644 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 /* Note: for machines with no snapshots, we always return FALSE
2647 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2648 * reasons :) */
2649
2650 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2651 ? FALSE
2652 : mData->mCurrentStateModified;
2653
2654 return S_OK;
2655}
2656
2657STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2658{
2659 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2660
2661 AutoCaller autoCaller(this);
2662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2663
2664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2665
2666 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2667 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2668
2669 return S_OK;
2670}
2671
2672STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2673{
2674 CheckComArgOutPointerValid(aClipboardMode);
2675
2676 AutoCaller autoCaller(this);
2677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2678
2679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2680
2681 *aClipboardMode = mHWData->mClipboardMode;
2682
2683 return S_OK;
2684}
2685
2686STDMETHODIMP
2687Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2688{
2689 HRESULT rc = S_OK;
2690
2691 AutoCaller autoCaller(this);
2692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2693
2694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2695
2696 alock.release();
2697 rc = onClipboardModeChange(aClipboardMode);
2698 alock.acquire();
2699 if (FAILED(rc)) return rc;
2700
2701 setModified(IsModified_MachineData);
2702 mHWData.backup();
2703 mHWData->mClipboardMode = aClipboardMode;
2704
2705 /* Save settings if online - todo why is this required?? */
2706 if (Global::IsOnline(mData->mMachineState))
2707 saveSettings(NULL);
2708
2709 return S_OK;
2710}
2711
2712STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2713{
2714 CheckComArgOutPointerValid(aDragAndDropMode);
2715
2716 AutoCaller autoCaller(this);
2717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2718
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 *aDragAndDropMode = mHWData->mDragAndDropMode;
2722
2723 return S_OK;
2724}
2725
2726STDMETHODIMP
2727Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2728{
2729 HRESULT rc = S_OK;
2730
2731 AutoCaller autoCaller(this);
2732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2733
2734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2735
2736 alock.release();
2737 rc = onDragAndDropModeChange(aDragAndDropMode);
2738 alock.acquire();
2739 if (FAILED(rc)) return rc;
2740
2741 setModified(IsModified_MachineData);
2742 mHWData.backup();
2743 mHWData->mDragAndDropMode = aDragAndDropMode;
2744
2745 /* Save settings if online - todo why is this required?? */
2746 if (Global::IsOnline(mData->mMachineState))
2747 saveSettings(NULL);
2748
2749 return S_OK;
2750}
2751
2752STDMETHODIMP
2753Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2754{
2755 CheckComArgOutPointerValid(aPatterns);
2756
2757 AutoCaller autoCaller(this);
2758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2759
2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2761
2762 try
2763 {
2764 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2765 }
2766 catch (...)
2767 {
2768 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2769 }
2770
2771 return S_OK;
2772}
2773
2774STDMETHODIMP
2775Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2776{
2777 AutoCaller autoCaller(this);
2778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2779
2780 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 HRESULT rc = checkStateDependency(MutableStateDep);
2783 if (FAILED(rc)) return rc;
2784
2785 setModified(IsModified_MachineData);
2786 mHWData.backup();
2787 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2788 return rc;
2789}
2790
2791STDMETHODIMP
2792Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2793{
2794 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2795
2796 AutoCaller autoCaller(this);
2797 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2798
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2802 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2803
2804 return S_OK;
2805}
2806
2807STDMETHODIMP
2808Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2809{
2810 CheckComArgOutPointerValid(aEnabled);
2811
2812 AutoCaller autoCaller(this);
2813 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2814
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 *aEnabled = mUserData->s.fTeleporterEnabled;
2818
2819 return S_OK;
2820}
2821
2822STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2823{
2824 AutoCaller autoCaller(this);
2825 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2826
2827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 /* Only allow it to be set to true when PoweredOff or Aborted.
2830 (Clearing it is always permitted.) */
2831 if ( aEnabled
2832 && mData->mRegistered
2833 && ( !isSessionMachine()
2834 || ( mData->mMachineState != MachineState_PoweredOff
2835 && mData->mMachineState != MachineState_Teleported
2836 && mData->mMachineState != MachineState_Aborted
2837 )
2838 )
2839 )
2840 return setError(VBOX_E_INVALID_VM_STATE,
2841 tr("The machine is not powered off (state is %s)"),
2842 Global::stringifyMachineState(mData->mMachineState));
2843
2844 setModified(IsModified_MachineData);
2845 mUserData.backup();
2846 mUserData->s.fTeleporterEnabled = !!aEnabled;
2847
2848 return S_OK;
2849}
2850
2851STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2852{
2853 CheckComArgOutPointerValid(aPort);
2854
2855 AutoCaller autoCaller(this);
2856 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2857
2858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2859
2860 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2861
2862 return S_OK;
2863}
2864
2865STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2866{
2867 if (aPort >= _64K)
2868 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2869
2870 AutoCaller autoCaller(this);
2871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2872
2873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2874
2875 HRESULT rc = checkStateDependency(MutableStateDep);
2876 if (FAILED(rc)) return rc;
2877
2878 setModified(IsModified_MachineData);
2879 mUserData.backup();
2880 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2881
2882 return S_OK;
2883}
2884
2885STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2886{
2887 CheckComArgOutPointerValid(aAddress);
2888
2889 AutoCaller autoCaller(this);
2890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2891
2892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2893
2894 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2895
2896 return S_OK;
2897}
2898
2899STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2900{
2901 AutoCaller autoCaller(this);
2902 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 HRESULT rc = checkStateDependency(MutableStateDep);
2907 if (FAILED(rc)) return rc;
2908
2909 setModified(IsModified_MachineData);
2910 mUserData.backup();
2911 mUserData->s.strTeleporterAddress = aAddress;
2912
2913 return S_OK;
2914}
2915
2916STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2917{
2918 CheckComArgOutPointerValid(aPassword);
2919
2920 AutoCaller autoCaller(this);
2921 HRESULT hrc = autoCaller.rc();
2922 if (SUCCEEDED(hrc))
2923 {
2924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2925 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2926 }
2927
2928 return hrc;
2929}
2930
2931STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2932{
2933 /*
2934 * Hash the password first.
2935 */
2936 Utf8Str strPassword(aPassword);
2937 if (!strPassword.isEmpty())
2938 {
2939 if (VBoxIsPasswordHashed(&strPassword))
2940 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2941 VBoxHashPassword(&strPassword);
2942 }
2943
2944 /*
2945 * Do the update.
2946 */
2947 AutoCaller autoCaller(this);
2948 HRESULT hrc = autoCaller.rc();
2949 if (SUCCEEDED(hrc))
2950 {
2951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2952 hrc = checkStateDependency(MutableStateDep);
2953 if (SUCCEEDED(hrc))
2954 {
2955 setModified(IsModified_MachineData);
2956 mUserData.backup();
2957 mUserData->s.strTeleporterPassword = strPassword;
2958 }
2959 }
2960
2961 return hrc;
2962}
2963
2964STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2965{
2966 CheckComArgOutPointerValid(aState);
2967
2968 AutoCaller autoCaller(this);
2969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2970
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 *aState = mUserData->s.enmFaultToleranceState;
2974 return S_OK;
2975}
2976
2977STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2978{
2979 AutoCaller autoCaller(this);
2980 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2981
2982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2983
2984 /* @todo deal with running state change. */
2985 HRESULT rc = checkStateDependency(MutableStateDep);
2986 if (FAILED(rc)) return rc;
2987
2988 setModified(IsModified_MachineData);
2989 mUserData.backup();
2990 mUserData->s.enmFaultToleranceState = aState;
2991 return S_OK;
2992}
2993
2994STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2995{
2996 CheckComArgOutPointerValid(aAddress);
2997
2998 AutoCaller autoCaller(this);
2999 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3000
3001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3002
3003 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3004 return S_OK;
3005}
3006
3007STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3008{
3009 AutoCaller autoCaller(this);
3010 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3011
3012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3013
3014 /* @todo deal with running state change. */
3015 HRESULT rc = checkStateDependency(MutableStateDep);
3016 if (FAILED(rc)) return rc;
3017
3018 setModified(IsModified_MachineData);
3019 mUserData.backup();
3020 mUserData->s.strFaultToleranceAddress = aAddress;
3021 return S_OK;
3022}
3023
3024STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3025{
3026 CheckComArgOutPointerValid(aPort);
3027
3028 AutoCaller autoCaller(this);
3029 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3030
3031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3032
3033 *aPort = mUserData->s.uFaultTolerancePort;
3034 return S_OK;
3035}
3036
3037STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3038{
3039 AutoCaller autoCaller(this);
3040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3041
3042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3043
3044 /* @todo deal with running state change. */
3045 HRESULT rc = checkStateDependency(MutableStateDep);
3046 if (FAILED(rc)) return rc;
3047
3048 setModified(IsModified_MachineData);
3049 mUserData.backup();
3050 mUserData->s.uFaultTolerancePort = aPort;
3051 return S_OK;
3052}
3053
3054STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3055{
3056 CheckComArgOutPointerValid(aPassword);
3057
3058 AutoCaller autoCaller(this);
3059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3060
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3064
3065 return S_OK;
3066}
3067
3068STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3069{
3070 AutoCaller autoCaller(this);
3071 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3072
3073 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3074
3075 /* @todo deal with running state change. */
3076 HRESULT rc = checkStateDependency(MutableStateDep);
3077 if (FAILED(rc)) return rc;
3078
3079 setModified(IsModified_MachineData);
3080 mUserData.backup();
3081 mUserData->s.strFaultTolerancePassword = aPassword;
3082
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3087{
3088 CheckComArgOutPointerValid(aInterval);
3089
3090 AutoCaller autoCaller(this);
3091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3092
3093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3094
3095 *aInterval = mUserData->s.uFaultToleranceInterval;
3096 return S_OK;
3097}
3098
3099STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3100{
3101 AutoCaller autoCaller(this);
3102 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3103
3104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3105
3106 /* @todo deal with running state change. */
3107 HRESULT rc = checkStateDependency(MutableStateDep);
3108 if (FAILED(rc)) return rc;
3109
3110 setModified(IsModified_MachineData);
3111 mUserData.backup();
3112 mUserData->s.uFaultToleranceInterval = aInterval;
3113 return S_OK;
3114}
3115
3116STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3117{
3118 CheckComArgOutPointerValid(aEnabled);
3119
3120 AutoCaller autoCaller(this);
3121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3122
3123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3124
3125 *aEnabled = mUserData->s.fRTCUseUTC;
3126
3127 return S_OK;
3128}
3129
3130STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3131{
3132 AutoCaller autoCaller(this);
3133 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3134
3135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3136
3137 /* Only allow it to be set to true when PoweredOff or Aborted.
3138 (Clearing it is always permitted.) */
3139 if ( aEnabled
3140 && mData->mRegistered
3141 && ( !isSessionMachine()
3142 || ( mData->mMachineState != MachineState_PoweredOff
3143 && mData->mMachineState != MachineState_Teleported
3144 && mData->mMachineState != MachineState_Aborted
3145 )
3146 )
3147 )
3148 return setError(VBOX_E_INVALID_VM_STATE,
3149 tr("The machine is not powered off (state is %s)"),
3150 Global::stringifyMachineState(mData->mMachineState));
3151
3152 setModified(IsModified_MachineData);
3153 mUserData.backup();
3154 mUserData->s.fRTCUseUTC = !!aEnabled;
3155
3156 return S_OK;
3157}
3158
3159STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3160{
3161 CheckComArgOutPointerValid(aEnabled);
3162
3163 AutoCaller autoCaller(this);
3164 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3165
3166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 *aEnabled = mHWData->mIOCacheEnabled;
3169
3170 return S_OK;
3171}
3172
3173STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3174{
3175 AutoCaller autoCaller(this);
3176 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3177
3178 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3179
3180 HRESULT rc = checkStateDependency(MutableStateDep);
3181 if (FAILED(rc)) return rc;
3182
3183 setModified(IsModified_MachineData);
3184 mHWData.backup();
3185 mHWData->mIOCacheEnabled = aEnabled;
3186
3187 return S_OK;
3188}
3189
3190STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3191{
3192 CheckComArgOutPointerValid(aIOCacheSize);
3193
3194 AutoCaller autoCaller(this);
3195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3196
3197 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3198
3199 *aIOCacheSize = mHWData->mIOCacheSize;
3200
3201 return S_OK;
3202}
3203
3204STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3205{
3206 AutoCaller autoCaller(this);
3207 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3208
3209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 HRESULT rc = checkStateDependency(MutableStateDep);
3212 if (FAILED(rc)) return rc;
3213
3214 setModified(IsModified_MachineData);
3215 mHWData.backup();
3216 mHWData->mIOCacheSize = aIOCacheSize;
3217
3218 return S_OK;
3219}
3220
3221
3222/**
3223 * @note Locks objects!
3224 */
3225STDMETHODIMP Machine::LockMachine(ISession *aSession,
3226 LockType_T lockType)
3227{
3228 CheckComArgNotNull(aSession);
3229
3230 AutoCaller autoCaller(this);
3231 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3232
3233 /* check the session state */
3234 SessionState_T state;
3235 HRESULT rc = aSession->COMGETTER(State)(&state);
3236 if (FAILED(rc)) return rc;
3237
3238 if (state != SessionState_Unlocked)
3239 return setError(VBOX_E_INVALID_OBJECT_STATE,
3240 tr("The given session is busy"));
3241
3242 // get the client's IInternalSessionControl interface
3243 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3244 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3245 E_INVALIDARG);
3246
3247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3248
3249 if (!mData->mRegistered)
3250 return setError(E_UNEXPECTED,
3251 tr("The machine '%s' is not registered"),
3252 mUserData->s.strName.c_str());
3253
3254 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3255
3256 SessionState_T oldState = mData->mSession.mState;
3257 /* Hack: in case the session is closing and there is a progress object
3258 * which allows waiting for the session to be closed, take the opportunity
3259 * and do a limited wait (max. 1 second). This helps a lot when the system
3260 * is busy and thus session closing can take a little while. */
3261 if ( mData->mSession.mState == SessionState_Unlocking
3262 && mData->mSession.mProgress)
3263 {
3264 alock.release();
3265 mData->mSession.mProgress->WaitForCompletion(1000);
3266 alock.acquire();
3267 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3268 }
3269
3270 // try again now
3271 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3272 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3273 )
3274 {
3275 // OK, share the session... we are now dealing with three processes:
3276 // 1) VBoxSVC (where this code runs);
3277 // 2) process C: the caller's client process (who wants a shared session);
3278 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3279
3280 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3281 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3282 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3283 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3284 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3285
3286 /*
3287 * Release the lock before calling the client process. It's safe here
3288 * since the only thing to do after we get the lock again is to add
3289 * the remote control to the list (which doesn't directly influence
3290 * anything).
3291 */
3292 alock.release();
3293
3294 // get the console of the session holding the write lock (this is a remote call)
3295 ComPtr<IConsole> pConsoleW;
3296 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3297 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3298 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3299 if (FAILED(rc))
3300 // the failure may occur w/o any error info (from RPC), so provide one
3301 return setError(VBOX_E_VM_ERROR,
3302 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3303
3304 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3305
3306 // share the session machine and W's console with the caller's session
3307 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3308 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3309 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3310
3311 if (FAILED(rc))
3312 // the failure may occur w/o any error info (from RPC), so provide one
3313 return setError(VBOX_E_VM_ERROR,
3314 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3315 alock.acquire();
3316
3317 // need to revalidate the state after acquiring the lock again
3318 if (mData->mSession.mState != SessionState_Locked)
3319 {
3320 pSessionControl->Uninitialize();
3321 return setError(VBOX_E_INVALID_SESSION_STATE,
3322 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3323 mUserData->s.strName.c_str());
3324 }
3325
3326 // add the caller's session to the list
3327 mData->mSession.mRemoteControls.push_back(pSessionControl);
3328 }
3329 else if ( mData->mSession.mState == SessionState_Locked
3330 || mData->mSession.mState == SessionState_Unlocking
3331 )
3332 {
3333 // sharing not permitted, or machine still unlocking:
3334 return setError(VBOX_E_INVALID_OBJECT_STATE,
3335 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3336 mUserData->s.strName.c_str());
3337 }
3338 else
3339 {
3340 // machine is not locked: then write-lock the machine (create the session machine)
3341
3342 // must not be busy
3343 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3344
3345 // get the caller's session PID
3346 RTPROCESS pid = NIL_RTPROCESS;
3347 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3348 pSessionControl->GetPID((ULONG*)&pid);
3349 Assert(pid != NIL_RTPROCESS);
3350
3351 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3352
3353 if (fLaunchingVMProcess)
3354 {
3355 // this machine is awaiting for a spawning session to be opened:
3356 // then the calling process must be the one that got started by
3357 // LaunchVMProcess()
3358
3359 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3360 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3361
3362 if (mData->mSession.mPID != pid)
3363 return setError(E_ACCESSDENIED,
3364 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3365 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3366 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3367 }
3368
3369 // create the mutable SessionMachine from the current machine
3370 ComObjPtr<SessionMachine> sessionMachine;
3371 sessionMachine.createObject();
3372 rc = sessionMachine->init(this);
3373 AssertComRC(rc);
3374
3375 /* NOTE: doing return from this function after this point but
3376 * before the end is forbidden since it may call SessionMachine::uninit()
3377 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3378 * lock while still holding the Machine lock in alock so that a deadlock
3379 * is possible due to the wrong lock order. */
3380
3381 if (SUCCEEDED(rc))
3382 {
3383 /*
3384 * Set the session state to Spawning to protect against subsequent
3385 * attempts to open a session and to unregister the machine after
3386 * we release the lock.
3387 */
3388 SessionState_T origState = mData->mSession.mState;
3389 mData->mSession.mState = SessionState_Spawning;
3390
3391 /*
3392 * Release the lock before calling the client process -- it will call
3393 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3394 * because the state is Spawning, so that LaunchVMProcess() and
3395 * LockMachine() calls will fail. This method, called before we
3396 * acquire the lock again, will fail because of the wrong PID.
3397 *
3398 * Note that mData->mSession.mRemoteControls accessed outside
3399 * the lock may not be modified when state is Spawning, so it's safe.
3400 */
3401 alock.release();
3402
3403 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3404 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3405 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3406
3407 /* The failure may occur w/o any error info (from RPC), so provide one */
3408 if (FAILED(rc))
3409 setError(VBOX_E_VM_ERROR,
3410 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3411
3412 if ( SUCCEEDED(rc)
3413 && fLaunchingVMProcess
3414 )
3415 {
3416 /* complete the remote session initialization */
3417
3418 /* get the console from the direct session */
3419 ComPtr<IConsole> console;
3420 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3421 ComAssertComRC(rc);
3422
3423 if (SUCCEEDED(rc) && !console)
3424 {
3425 ComAssert(!!console);
3426 rc = E_FAIL;
3427 }
3428
3429 /* assign machine & console to the remote session */
3430 if (SUCCEEDED(rc))
3431 {
3432 /*
3433 * after LaunchVMProcess(), the first and the only
3434 * entry in remoteControls is that remote session
3435 */
3436 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3437 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3438 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3439
3440 /* The failure may occur w/o any error info (from RPC), so provide one */
3441 if (FAILED(rc))
3442 setError(VBOX_E_VM_ERROR,
3443 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3444 }
3445
3446 if (FAILED(rc))
3447 pSessionControl->Uninitialize();
3448 }
3449
3450 /* acquire the lock again */
3451 alock.acquire();
3452
3453 /* Restore the session state */
3454 mData->mSession.mState = origState;
3455 }
3456
3457 // finalize spawning anyway (this is why we don't return on errors above)
3458 if (fLaunchingVMProcess)
3459 {
3460 /* Note that the progress object is finalized later */
3461 /** @todo Consider checking mData->mSession.mProgress for cancellation
3462 * around here. */
3463
3464 /* We don't reset mSession.mPID here because it is necessary for
3465 * SessionMachine::uninit() to reap the child process later. */
3466
3467 if (FAILED(rc))
3468 {
3469 /* Close the remote session, remove the remote control from the list
3470 * and reset session state to Closed (@note keep the code in sync
3471 * with the relevant part in openSession()). */
3472
3473 Assert(mData->mSession.mRemoteControls.size() == 1);
3474 if (mData->mSession.mRemoteControls.size() == 1)
3475 {
3476 ErrorInfoKeeper eik;
3477 mData->mSession.mRemoteControls.front()->Uninitialize();
3478 }
3479
3480 mData->mSession.mRemoteControls.clear();
3481 mData->mSession.mState = SessionState_Unlocked;
3482 }
3483 }
3484 else
3485 {
3486 /* memorize PID of the directly opened session */
3487 if (SUCCEEDED(rc))
3488 mData->mSession.mPID = pid;
3489 }
3490
3491 if (SUCCEEDED(rc))
3492 {
3493 /* memorize the direct session control and cache IUnknown for it */
3494 mData->mSession.mDirectControl = pSessionControl;
3495 mData->mSession.mState = SessionState_Locked;
3496 /* associate the SessionMachine with this Machine */
3497 mData->mSession.mMachine = sessionMachine;
3498
3499 /* request an IUnknown pointer early from the remote party for later
3500 * identity checks (it will be internally cached within mDirectControl
3501 * at least on XPCOM) */
3502 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3503 NOREF(unk);
3504 }
3505
3506 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3507 * would break the lock order */
3508 alock.release();
3509
3510 /* uninitialize the created session machine on failure */
3511 if (FAILED(rc))
3512 sessionMachine->uninit();
3513
3514 }
3515
3516 if (SUCCEEDED(rc))
3517 {
3518 /*
3519 * tell the client watcher thread to update the set of
3520 * machines that have open sessions
3521 */
3522 mParent->updateClientWatcher();
3523
3524 if (oldState != SessionState_Locked)
3525 /* fire an event */
3526 mParent->onSessionStateChange(getId(), SessionState_Locked);
3527 }
3528
3529 return rc;
3530}
3531
3532/**
3533 * @note Locks objects!
3534 */
3535STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3536 IN_BSTR aType,
3537 IN_BSTR aEnvironment,
3538 IProgress **aProgress)
3539{
3540 CheckComArgStrNotEmptyOrNull(aType);
3541 Utf8Str strType(aType);
3542 Utf8Str strEnvironment(aEnvironment);
3543 /* "emergencystop" doesn't need the session, so skip the checks/interface
3544 * retrieval. This code doesn't quite fit in here, but introducing a
3545 * special API method would be even more effort, and would require explicit
3546 * support by every API client. It's better to hide the feature a bit. */
3547 if (strType != "emergencystop")
3548 CheckComArgNotNull(aSession);
3549 CheckComArgOutPointerValid(aProgress);
3550
3551 AutoCaller autoCaller(this);
3552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3553
3554 ComPtr<IInternalSessionControl> control;
3555 HRESULT rc = S_OK;
3556
3557 if (strType != "emergencystop")
3558 {
3559 /* check the session state */
3560 SessionState_T state;
3561 rc = aSession->COMGETTER(State)(&state);
3562 if (FAILED(rc))
3563 return rc;
3564
3565 if (state != SessionState_Unlocked)
3566 return setError(VBOX_E_INVALID_OBJECT_STATE,
3567 tr("The given session is busy"));
3568
3569 /* get the IInternalSessionControl interface */
3570 control = aSession;
3571 ComAssertMsgRet(!control.isNull(),
3572 ("No IInternalSessionControl interface"),
3573 E_INVALIDARG);
3574 }
3575
3576 /* get the teleporter enable state for the progress object init. */
3577 BOOL fTeleporterEnabled;
3578 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3579 if (FAILED(rc))
3580 return rc;
3581
3582 /* create a progress object */
3583 if (strType != "emergencystop")
3584 {
3585 ComObjPtr<ProgressProxy> progress;
3586 progress.createObject();
3587 rc = progress->init(mParent,
3588 static_cast<IMachine*>(this),
3589 Bstr(tr("Starting VM")).raw(),
3590 TRUE /* aCancelable */,
3591 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3592 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3593 2 /* uFirstOperationWeight */,
3594 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3595
3596 if (SUCCEEDED(rc))
3597 {
3598 rc = launchVMProcess(control, strType, strEnvironment, progress);
3599 if (SUCCEEDED(rc))
3600 {
3601 progress.queryInterfaceTo(aProgress);
3602
3603 /* signal the client watcher thread */
3604 mParent->updateClientWatcher();
3605
3606 /* fire an event */
3607 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3608 }
3609 }
3610 }
3611 else
3612 {
3613 /* no progress object - either instant success or failure */
3614 *aProgress = NULL;
3615
3616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3617
3618 if (mData->mSession.mState != SessionState_Locked)
3619 return setError(VBOX_E_INVALID_OBJECT_STATE,
3620 tr("The machine '%s' is not locked by a session"),
3621 mUserData->s.strName.c_str());
3622
3623 /* must have a VM process associated - do not kill normal API clients
3624 * with an open session */
3625 if (!Global::IsOnline(mData->mMachineState))
3626 return setError(VBOX_E_INVALID_OBJECT_STATE,
3627 tr("The machine '%s' does not have a VM process"),
3628 mUserData->s.strName.c_str());
3629
3630 /* forcibly terminate the VM process */
3631 if (mData->mSession.mPID != NIL_RTPROCESS)
3632 RTProcTerminate(mData->mSession.mPID);
3633
3634 /* signal the client watcher thread, as most likely the client has
3635 * been terminated */
3636 mParent->updateClientWatcher();
3637 }
3638
3639 return rc;
3640}
3641
3642STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3643{
3644 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3645 return setError(E_INVALIDARG,
3646 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3647 aPosition, SchemaDefs::MaxBootPosition);
3648
3649 if (aDevice == DeviceType_USB)
3650 return setError(E_NOTIMPL,
3651 tr("Booting from USB device is currently not supported"));
3652
3653 AutoCaller autoCaller(this);
3654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3655
3656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3657
3658 HRESULT rc = checkStateDependency(MutableStateDep);
3659 if (FAILED(rc)) return rc;
3660
3661 setModified(IsModified_MachineData);
3662 mHWData.backup();
3663 mHWData->mBootOrder[aPosition - 1] = aDevice;
3664
3665 return S_OK;
3666}
3667
3668STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3669{
3670 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3671 return setError(E_INVALIDARG,
3672 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3673 aPosition, SchemaDefs::MaxBootPosition);
3674
3675 AutoCaller autoCaller(this);
3676 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3677
3678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3679
3680 *aDevice = mHWData->mBootOrder[aPosition - 1];
3681
3682 return S_OK;
3683}
3684
3685STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3686 LONG aControllerPort,
3687 LONG aDevice,
3688 DeviceType_T aType,
3689 IMedium *aMedium)
3690{
3691 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3692 aControllerName, aControllerPort, aDevice, aType, aMedium));
3693
3694 CheckComArgStrNotEmptyOrNull(aControllerName);
3695
3696 AutoCaller autoCaller(this);
3697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3698
3699 // request the host lock first, since might be calling Host methods for getting host drives;
3700 // next, protect the media tree all the while we're in here, as well as our member variables
3701 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3702 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3703
3704 HRESULT rc = checkStateDependency(MutableStateDep);
3705 if (FAILED(rc)) return rc;
3706
3707 /// @todo NEWMEDIA implicit machine registration
3708 if (!mData->mRegistered)
3709 return setError(VBOX_E_INVALID_OBJECT_STATE,
3710 tr("Cannot attach storage devices to an unregistered machine"));
3711
3712 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3713
3714 /* Check for an existing controller. */
3715 ComObjPtr<StorageController> ctl;
3716 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3717 if (FAILED(rc)) return rc;
3718
3719 StorageControllerType_T ctrlType;
3720 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3721 if (FAILED(rc))
3722 return setError(E_FAIL,
3723 tr("Could not get type of controller '%ls'"),
3724 aControllerName);
3725
3726 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3727 bool fHotplug = false;
3728 if (Global::IsOnlineOrTransient(mData->mMachineState))
3729 fHotplug = true;
3730
3731 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3732 return setError(VBOX_E_INVALID_VM_STATE,
3733 tr("Controller '%ls' does not support hotplugging"),
3734 aControllerName);
3735
3736 // check that the port and device are not out of range
3737 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3738 if (FAILED(rc)) return rc;
3739
3740 /* check if the device slot is already busy */
3741 MediumAttachment *pAttachTemp;
3742 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3743 aControllerName,
3744 aControllerPort,
3745 aDevice)))
3746 {
3747 Medium *pMedium = pAttachTemp->getMedium();
3748 if (pMedium)
3749 {
3750 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3751 return setError(VBOX_E_OBJECT_IN_USE,
3752 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3753 pMedium->getLocationFull().c_str(),
3754 aControllerPort,
3755 aDevice,
3756 aControllerName);
3757 }
3758 else
3759 return setError(VBOX_E_OBJECT_IN_USE,
3760 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3761 aControllerPort, aDevice, aControllerName);
3762 }
3763
3764 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3765 if (aMedium && medium.isNull())
3766 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3767
3768 AutoCaller mediumCaller(medium);
3769 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3770
3771 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3772
3773 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3774 && !medium.isNull()
3775 )
3776 return setError(VBOX_E_OBJECT_IN_USE,
3777 tr("Medium '%s' is already attached to this virtual machine"),
3778 medium->getLocationFull().c_str());
3779
3780 if (!medium.isNull())
3781 {
3782 MediumType_T mtype = medium->getType();
3783 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3784 // For DVDs it's not written to the config file, so needs no global config
3785 // version bump. For floppies it's a new attribute "type", which is ignored
3786 // by older VirtualBox version, so needs no global config version bump either.
3787 // For hard disks this type is not accepted.
3788 if (mtype == MediumType_MultiAttach)
3789 {
3790 // This type is new with VirtualBox 4.0 and therefore requires settings
3791 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3792 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3793 // two reasons: The medium type is a property of the media registry tree, which
3794 // can reside in the global config file (for pre-4.0 media); we would therefore
3795 // possibly need to bump the global config version. We don't want to do that though
3796 // because that might make downgrading to pre-4.0 impossible.
3797 // As a result, we can only use these two new types if the medium is NOT in the
3798 // global registry:
3799 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3800 if ( medium->isInRegistry(uuidGlobalRegistry)
3801 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3802 )
3803 return setError(VBOX_E_INVALID_OBJECT_STATE,
3804 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3805 "to machines that were created with VirtualBox 4.0 or later"),
3806 medium->getLocationFull().c_str());
3807 }
3808 }
3809
3810 bool fIndirect = false;
3811 if (!medium.isNull())
3812 fIndirect = medium->isReadOnly();
3813 bool associate = true;
3814
3815 do
3816 {
3817 if ( aType == DeviceType_HardDisk
3818 && mMediaData.isBackedUp())
3819 {
3820 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3821
3822 /* check if the medium was attached to the VM before we started
3823 * changing attachments in which case the attachment just needs to
3824 * be restored */
3825 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3826 {
3827 AssertReturn(!fIndirect, E_FAIL);
3828
3829 /* see if it's the same bus/channel/device */
3830 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3831 {
3832 /* the simplest case: restore the whole attachment
3833 * and return, nothing else to do */
3834 mMediaData->mAttachments.push_back(pAttachTemp);
3835 return S_OK;
3836 }
3837
3838 /* bus/channel/device differ; we need a new attachment object,
3839 * but don't try to associate it again */
3840 associate = false;
3841 break;
3842 }
3843 }
3844
3845 /* go further only if the attachment is to be indirect */
3846 if (!fIndirect)
3847 break;
3848
3849 /* perform the so called smart attachment logic for indirect
3850 * attachments. Note that smart attachment is only applicable to base
3851 * hard disks. */
3852
3853 if (medium->getParent().isNull())
3854 {
3855 /* first, investigate the backup copy of the current hard disk
3856 * attachments to make it possible to re-attach existing diffs to
3857 * another device slot w/o losing their contents */
3858 if (mMediaData.isBackedUp())
3859 {
3860 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3861
3862 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3863 uint32_t foundLevel = 0;
3864
3865 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3866 it != oldAtts.end();
3867 ++it)
3868 {
3869 uint32_t level = 0;
3870 MediumAttachment *pAttach = *it;
3871 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3872 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3873 if (pMedium.isNull())
3874 continue;
3875
3876 if (pMedium->getBase(&level) == medium)
3877 {
3878 /* skip the hard disk if its currently attached (we
3879 * cannot attach the same hard disk twice) */
3880 if (findAttachment(mMediaData->mAttachments,
3881 pMedium))
3882 continue;
3883
3884 /* matched device, channel and bus (i.e. attached to the
3885 * same place) will win and immediately stop the search;
3886 * otherwise the attachment that has the youngest
3887 * descendant of medium will be used
3888 */
3889 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3890 {
3891 /* the simplest case: restore the whole attachment
3892 * and return, nothing else to do */
3893 mMediaData->mAttachments.push_back(*it);
3894 return S_OK;
3895 }
3896 else if ( foundIt == oldAtts.end()
3897 || level > foundLevel /* prefer younger */
3898 )
3899 {
3900 foundIt = it;
3901 foundLevel = level;
3902 }
3903 }
3904 }
3905
3906 if (foundIt != oldAtts.end())
3907 {
3908 /* use the previously attached hard disk */
3909 medium = (*foundIt)->getMedium();
3910 mediumCaller.attach(medium);
3911 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3912 mediumLock.attach(medium);
3913 /* not implicit, doesn't require association with this VM */
3914 fIndirect = false;
3915 associate = false;
3916 /* go right to the MediumAttachment creation */
3917 break;
3918 }
3919 }
3920
3921 /* must give up the medium lock and medium tree lock as below we
3922 * go over snapshots, which needs a lock with higher lock order. */
3923 mediumLock.release();
3924 treeLock.release();
3925
3926 /* then, search through snapshots for the best diff in the given
3927 * hard disk's chain to base the new diff on */
3928
3929 ComObjPtr<Medium> base;
3930 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3931 while (snap)
3932 {
3933 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3934
3935 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3936
3937 MediumAttachment *pAttachFound = NULL;
3938 uint32_t foundLevel = 0;
3939
3940 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3941 it != snapAtts.end();
3942 ++it)
3943 {
3944 MediumAttachment *pAttach = *it;
3945 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3946 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3947 if (pMedium.isNull())
3948 continue;
3949
3950 uint32_t level = 0;
3951 if (pMedium->getBase(&level) == medium)
3952 {
3953 /* matched device, channel and bus (i.e. attached to the
3954 * same place) will win and immediately stop the search;
3955 * otherwise the attachment that has the youngest
3956 * descendant of medium will be used
3957 */
3958 if ( pAttach->getDevice() == aDevice
3959 && pAttach->getPort() == aControllerPort
3960 && pAttach->getControllerName() == aControllerName
3961 )
3962 {
3963 pAttachFound = pAttach;
3964 break;
3965 }
3966 else if ( !pAttachFound
3967 || level > foundLevel /* prefer younger */
3968 )
3969 {
3970 pAttachFound = pAttach;
3971 foundLevel = level;
3972 }
3973 }
3974 }
3975
3976 if (pAttachFound)
3977 {
3978 base = pAttachFound->getMedium();
3979 break;
3980 }
3981
3982 snap = snap->getParent();
3983 }
3984
3985 /* re-lock medium tree and the medium, as we need it below */
3986 treeLock.acquire();
3987 mediumLock.acquire();
3988
3989 /* found a suitable diff, use it as a base */
3990 if (!base.isNull())
3991 {
3992 medium = base;
3993 mediumCaller.attach(medium);
3994 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3995 mediumLock.attach(medium);
3996 }
3997 }
3998
3999 Utf8Str strFullSnapshotFolder;
4000 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4001
4002 ComObjPtr<Medium> diff;
4003 diff.createObject();
4004 // store this diff in the same registry as the parent
4005 Guid uuidRegistryParent;
4006 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4007 {
4008 // parent image has no registry: this can happen if we're attaching a new immutable
4009 // image that has not yet been attached (medium then points to the base and we're
4010 // creating the diff image for the immutable, and the parent is not yet registered);
4011 // put the parent in the machine registry then
4012 mediumLock.release();
4013 treeLock.release();
4014 alock.release();
4015 addMediumToRegistry(medium);
4016 alock.acquire();
4017 treeLock.acquire();
4018 mediumLock.acquire();
4019 medium->getFirstRegistryMachineId(uuidRegistryParent);
4020 }
4021 rc = diff->init(mParent,
4022 medium->getPreferredDiffFormat(),
4023 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4024 uuidRegistryParent);
4025 if (FAILED(rc)) return rc;
4026
4027 /* Apply the normal locking logic to the entire chain. */
4028 MediumLockList *pMediumLockList(new MediumLockList());
4029 mediumLock.release();
4030 treeLock.release();
4031 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4032 true /* fMediumLockWrite */,
4033 medium,
4034 *pMediumLockList);
4035 treeLock.acquire();
4036 mediumLock.acquire();
4037 if (SUCCEEDED(rc))
4038 {
4039 mediumLock.release();
4040 treeLock.release();
4041 rc = pMediumLockList->Lock();
4042 treeLock.acquire();
4043 mediumLock.acquire();
4044 if (FAILED(rc))
4045 setError(rc,
4046 tr("Could not lock medium when creating diff '%s'"),
4047 diff->getLocationFull().c_str());
4048 else
4049 {
4050 /* will release the lock before the potentially lengthy
4051 * operation, so protect with the special state */
4052 MachineState_T oldState = mData->mMachineState;
4053 setMachineState(MachineState_SettingUp);
4054
4055 mediumLock.release();
4056 treeLock.release();
4057 alock.release();
4058
4059 rc = medium->createDiffStorage(diff,
4060 MediumVariant_Standard,
4061 pMediumLockList,
4062 NULL /* aProgress */,
4063 true /* aWait */);
4064
4065 alock.acquire();
4066 treeLock.acquire();
4067 mediumLock.acquire();
4068
4069 setMachineState(oldState);
4070 }
4071 }
4072
4073 /* Unlock the media and free the associated memory. */
4074 delete pMediumLockList;
4075
4076 if (FAILED(rc)) return rc;
4077
4078 /* use the created diff for the actual attachment */
4079 medium = diff;
4080 mediumCaller.attach(medium);
4081 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4082 mediumLock.attach(medium);
4083 }
4084 while (0);
4085
4086 ComObjPtr<MediumAttachment> attachment;
4087 attachment.createObject();
4088 rc = attachment->init(this,
4089 medium,
4090 aControllerName,
4091 aControllerPort,
4092 aDevice,
4093 aType,
4094 fIndirect,
4095 false /* fPassthrough */,
4096 false /* fTempEject */,
4097 false /* fNonRotational */,
4098 false /* fDiscard */,
4099 Utf8Str::Empty);
4100 if (FAILED(rc)) return rc;
4101
4102 if (associate && !medium.isNull())
4103 {
4104 // as the last step, associate the medium to the VM
4105 rc = medium->addBackReference(mData->mUuid);
4106 // here we can fail because of Deleting, or being in process of creating a Diff
4107 if (FAILED(rc)) return rc;
4108
4109 mediumLock.release();
4110 treeLock.release();
4111 alock.release();
4112 addMediumToRegistry(medium);
4113 alock.acquire();
4114 treeLock.acquire();
4115 mediumLock.acquire();
4116 }
4117
4118 /* success: finally remember the attachment */
4119 setModified(IsModified_Storage);
4120 mMediaData.backup();
4121 mMediaData->mAttachments.push_back(attachment);
4122
4123 mediumLock.release();
4124 treeLock.release();
4125 alock.release();
4126
4127 if (fHotplug)
4128 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4129
4130 mParent->saveModifiedRegistries();
4131
4132 return rc;
4133}
4134
4135STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4136 LONG aDevice)
4137{
4138 CheckComArgStrNotEmptyOrNull(aControllerName);
4139
4140 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4141 aControllerName, aControllerPort, aDevice));
4142
4143 AutoCaller autoCaller(this);
4144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4145
4146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4147
4148 HRESULT rc = checkStateDependency(MutableStateDep);
4149 if (FAILED(rc)) return rc;
4150
4151 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4152
4153 /* Check for an existing controller. */
4154 ComObjPtr<StorageController> ctl;
4155 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4156 if (FAILED(rc)) return rc;
4157
4158 StorageControllerType_T ctrlType;
4159 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4160 if (FAILED(rc))
4161 return setError(E_FAIL,
4162 tr("Could not get type of controller '%ls'"),
4163 aControllerName);
4164
4165 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4166 bool fHotplug = false;
4167 if (Global::IsOnlineOrTransient(mData->mMachineState))
4168 fHotplug = true;
4169
4170 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4171 return setError(VBOX_E_INVALID_VM_STATE,
4172 tr("Controller '%ls' does not support hotplugging"),
4173 aControllerName);
4174
4175 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4176 aControllerName,
4177 aControllerPort,
4178 aDevice);
4179 if (!pAttach)
4180 return setError(VBOX_E_OBJECT_NOT_FOUND,
4181 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4182 aDevice, aControllerPort, aControllerName);
4183
4184 /*
4185 * The VM has to detach the device before we delete any implicit diffs.
4186 * If this fails we can roll back without loosing data.
4187 */
4188 if (fHotplug)
4189 {
4190 alock.release();
4191 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4192 alock.acquire();
4193 }
4194 if (FAILED(rc)) return rc;
4195
4196 /* If we are here everything went well and we can delete the implicit now. */
4197 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4198
4199 alock.release();
4200
4201 mParent->saveModifiedRegistries();
4202
4203 return rc;
4204}
4205
4206STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4207 LONG aDevice, BOOL aPassthrough)
4208{
4209 CheckComArgStrNotEmptyOrNull(aControllerName);
4210
4211 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4212 aControllerName, aControllerPort, aDevice, aPassthrough));
4213
4214 AutoCaller autoCaller(this);
4215 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4216
4217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4218
4219 HRESULT rc = checkStateDependency(MutableStateDep);
4220 if (FAILED(rc)) return rc;
4221
4222 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4223
4224 if (Global::IsOnlineOrTransient(mData->mMachineState))
4225 return setError(VBOX_E_INVALID_VM_STATE,
4226 tr("Invalid machine state: %s"),
4227 Global::stringifyMachineState(mData->mMachineState));
4228
4229 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4230 aControllerName,
4231 aControllerPort,
4232 aDevice);
4233 if (!pAttach)
4234 return setError(VBOX_E_OBJECT_NOT_FOUND,
4235 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4236 aDevice, aControllerPort, aControllerName);
4237
4238
4239 setModified(IsModified_Storage);
4240 mMediaData.backup();
4241
4242 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4243
4244 if (pAttach->getType() != DeviceType_DVD)
4245 return setError(E_INVALIDARG,
4246 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4247 aDevice, aControllerPort, aControllerName);
4248 pAttach->updatePassthrough(!!aPassthrough);
4249
4250 return S_OK;
4251}
4252
4253STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4254 LONG aDevice, BOOL aTemporaryEject)
4255{
4256 CheckComArgStrNotEmptyOrNull(aControllerName);
4257
4258 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4259 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4260
4261 AutoCaller autoCaller(this);
4262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4263
4264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4265
4266 HRESULT rc = checkStateDependency(MutableStateDep);
4267 if (FAILED(rc)) return rc;
4268
4269 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4270 aControllerName,
4271 aControllerPort,
4272 aDevice);
4273 if (!pAttach)
4274 return setError(VBOX_E_OBJECT_NOT_FOUND,
4275 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4276 aDevice, aControllerPort, aControllerName);
4277
4278
4279 setModified(IsModified_Storage);
4280 mMediaData.backup();
4281
4282 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4283
4284 if (pAttach->getType() != DeviceType_DVD)
4285 return setError(E_INVALIDARG,
4286 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4287 aDevice, aControllerPort, aControllerName);
4288 pAttach->updateTempEject(!!aTemporaryEject);
4289
4290 return S_OK;
4291}
4292
4293STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4294 LONG aDevice, BOOL aNonRotational)
4295{
4296 CheckComArgStrNotEmptyOrNull(aControllerName);
4297
4298 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4299 aControllerName, aControllerPort, aDevice, aNonRotational));
4300
4301 AutoCaller autoCaller(this);
4302 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4303
4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4305
4306 HRESULT rc = checkStateDependency(MutableStateDep);
4307 if (FAILED(rc)) return rc;
4308
4309 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4310
4311 if (Global::IsOnlineOrTransient(mData->mMachineState))
4312 return setError(VBOX_E_INVALID_VM_STATE,
4313 tr("Invalid machine state: %s"),
4314 Global::stringifyMachineState(mData->mMachineState));
4315
4316 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4317 aControllerName,
4318 aControllerPort,
4319 aDevice);
4320 if (!pAttach)
4321 return setError(VBOX_E_OBJECT_NOT_FOUND,
4322 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4323 aDevice, aControllerPort, aControllerName);
4324
4325
4326 setModified(IsModified_Storage);
4327 mMediaData.backup();
4328
4329 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4330
4331 if (pAttach->getType() != DeviceType_HardDisk)
4332 return setError(E_INVALIDARG,
4333 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"),
4334 aDevice, aControllerPort, aControllerName);
4335 pAttach->updateNonRotational(!!aNonRotational);
4336
4337 return S_OK;
4338}
4339
4340STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4341 LONG aDevice, BOOL aDiscard)
4342{
4343 CheckComArgStrNotEmptyOrNull(aControllerName);
4344
4345 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4346 aControllerName, aControllerPort, aDevice, aDiscard));
4347
4348 AutoCaller autoCaller(this);
4349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4350
4351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4352
4353 HRESULT rc = checkStateDependency(MutableStateDep);
4354 if (FAILED(rc)) return rc;
4355
4356 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4357
4358 if (Global::IsOnlineOrTransient(mData->mMachineState))
4359 return setError(VBOX_E_INVALID_VM_STATE,
4360 tr("Invalid machine state: %s"),
4361 Global::stringifyMachineState(mData->mMachineState));
4362
4363 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4364 aControllerName,
4365 aControllerPort,
4366 aDevice);
4367 if (!pAttach)
4368 return setError(VBOX_E_OBJECT_NOT_FOUND,
4369 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4370 aDevice, aControllerPort, aControllerName);
4371
4372
4373 setModified(IsModified_Storage);
4374 mMediaData.backup();
4375
4376 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4377
4378 if (pAttach->getType() != DeviceType_HardDisk)
4379 return setError(E_INVALIDARG,
4380 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"),
4381 aDevice, aControllerPort, aControllerName);
4382 pAttach->updateDiscard(!!aDiscard);
4383
4384 return S_OK;
4385}
4386
4387STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4388 LONG aDevice)
4389{
4390 int rc = S_OK;
4391 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4392 aControllerName, aControllerPort, aDevice));
4393
4394 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4395
4396 return rc;
4397}
4398
4399STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4400 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4401{
4402 CheckComArgStrNotEmptyOrNull(aControllerName);
4403
4404 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4405 aControllerName, aControllerPort, aDevice));
4406
4407 AutoCaller autoCaller(this);
4408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4409
4410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4411
4412 HRESULT rc = checkStateDependency(MutableStateDep);
4413 if (FAILED(rc)) return rc;
4414
4415 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4416
4417 if (Global::IsOnlineOrTransient(mData->mMachineState))
4418 return setError(VBOX_E_INVALID_VM_STATE,
4419 tr("Invalid machine state: %s"),
4420 Global::stringifyMachineState(mData->mMachineState));
4421
4422 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4423 aControllerName,
4424 aControllerPort,
4425 aDevice);
4426 if (!pAttach)
4427 return setError(VBOX_E_OBJECT_NOT_FOUND,
4428 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4429 aDevice, aControllerPort, aControllerName);
4430
4431
4432 setModified(IsModified_Storage);
4433 mMediaData.backup();
4434
4435 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4436 if (aBandwidthGroup && group.isNull())
4437 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4438
4439 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4440
4441 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4442 if (strBandwidthGroupOld.isNotEmpty())
4443 {
4444 /* Get the bandwidth group object and release it - this must not fail. */
4445 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4446 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4447 Assert(SUCCEEDED(rc));
4448
4449 pBandwidthGroupOld->release();
4450 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4451 }
4452
4453 if (!group.isNull())
4454 {
4455 group->reference();
4456 pAttach->updateBandwidthGroup(group->getName());
4457 }
4458
4459 return S_OK;
4460}
4461
4462STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4463 LONG aControllerPort,
4464 LONG aDevice,
4465 DeviceType_T aType)
4466{
4467 HRESULT rc = S_OK;
4468
4469 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4470 aControllerName, aControllerPort, aDevice, aType));
4471
4472 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4473
4474 return rc;
4475}
4476
4477
4478
4479STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4480 LONG aControllerPort,
4481 LONG aDevice,
4482 BOOL aForce)
4483{
4484 int rc = S_OK;
4485 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4486 aControllerName, aControllerPort, aForce));
4487
4488 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4489
4490 return rc;
4491}
4492
4493STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4494 LONG aControllerPort,
4495 LONG aDevice,
4496 IMedium *aMedium,
4497 BOOL aForce)
4498{
4499 int rc = S_OK;
4500 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4501 aControllerName, aControllerPort, aDevice, aForce));
4502
4503 CheckComArgStrNotEmptyOrNull(aControllerName);
4504
4505 AutoCaller autoCaller(this);
4506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4507
4508 // request the host lock first, since might be calling Host methods for getting host drives;
4509 // next, protect the media tree all the while we're in here, as well as our member variables
4510 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4511 this->lockHandle(),
4512 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4513
4514 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4515 aControllerName,
4516 aControllerPort,
4517 aDevice);
4518 if (pAttach.isNull())
4519 return setError(VBOX_E_OBJECT_NOT_FOUND,
4520 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4521 aDevice, aControllerPort, aControllerName);
4522
4523 /* Remember previously mounted medium. The medium before taking the
4524 * backup is not necessarily the same thing. */
4525 ComObjPtr<Medium> oldmedium;
4526 oldmedium = pAttach->getMedium();
4527
4528 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4529 if (aMedium && pMedium.isNull())
4530 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4531
4532 AutoCaller mediumCaller(pMedium);
4533 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4534
4535 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4536 if (pMedium)
4537 {
4538 DeviceType_T mediumType = pAttach->getType();
4539 switch (mediumType)
4540 {
4541 case DeviceType_DVD:
4542 case DeviceType_Floppy:
4543 break;
4544
4545 default:
4546 return setError(VBOX_E_INVALID_OBJECT_STATE,
4547 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4548 aControllerPort,
4549 aDevice,
4550 aControllerName);
4551 }
4552 }
4553
4554 setModified(IsModified_Storage);
4555 mMediaData.backup();
4556
4557 {
4558 // The backup operation makes the pAttach reference point to the
4559 // old settings. Re-get the correct reference.
4560 pAttach = findAttachment(mMediaData->mAttachments,
4561 aControllerName,
4562 aControllerPort,
4563 aDevice);
4564 if (!oldmedium.isNull())
4565 oldmedium->removeBackReference(mData->mUuid);
4566 if (!pMedium.isNull())
4567 {
4568 pMedium->addBackReference(mData->mUuid);
4569
4570 mediumLock.release();
4571 multiLock.release();
4572 addMediumToRegistry(pMedium);
4573 multiLock.acquire();
4574 mediumLock.acquire();
4575 }
4576
4577 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4578 pAttach->updateMedium(pMedium);
4579 }
4580
4581 setModified(IsModified_Storage);
4582
4583 mediumLock.release();
4584 multiLock.release();
4585 rc = onMediumChange(pAttach, aForce);
4586 multiLock.acquire();
4587 mediumLock.acquire();
4588
4589 /* On error roll back this change only. */
4590 if (FAILED(rc))
4591 {
4592 if (!pMedium.isNull())
4593 pMedium->removeBackReference(mData->mUuid);
4594 pAttach = findAttachment(mMediaData->mAttachments,
4595 aControllerName,
4596 aControllerPort,
4597 aDevice);
4598 /* If the attachment is gone in the meantime, bail out. */
4599 if (pAttach.isNull())
4600 return rc;
4601 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4602 if (!oldmedium.isNull())
4603 oldmedium->addBackReference(mData->mUuid);
4604 pAttach->updateMedium(oldmedium);
4605 }
4606
4607 mediumLock.release();
4608 multiLock.release();
4609
4610 mParent->saveModifiedRegistries();
4611
4612 return rc;
4613}
4614
4615STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4616 LONG aControllerPort,
4617 LONG aDevice,
4618 IMedium **aMedium)
4619{
4620 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4621 aControllerName, aControllerPort, aDevice));
4622
4623 CheckComArgStrNotEmptyOrNull(aControllerName);
4624 CheckComArgOutPointerValid(aMedium);
4625
4626 AutoCaller autoCaller(this);
4627 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4628
4629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4630
4631 *aMedium = NULL;
4632
4633 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4634 aControllerName,
4635 aControllerPort,
4636 aDevice);
4637 if (pAttach.isNull())
4638 return setError(VBOX_E_OBJECT_NOT_FOUND,
4639 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4640 aDevice, aControllerPort, aControllerName);
4641
4642 pAttach->getMedium().queryInterfaceTo(aMedium);
4643
4644 return S_OK;
4645}
4646
4647STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4648{
4649 CheckComArgOutPointerValid(port);
4650 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4651
4652 AutoCaller autoCaller(this);
4653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4654
4655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4656
4657 mSerialPorts[slot].queryInterfaceTo(port);
4658
4659 return S_OK;
4660}
4661
4662STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4663{
4664 CheckComArgOutPointerValid(port);
4665 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4666
4667 AutoCaller autoCaller(this);
4668 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4669
4670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4671
4672 mParallelPorts[slot].queryInterfaceTo(port);
4673
4674 return S_OK;
4675}
4676
4677STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4678{
4679 CheckComArgOutPointerValid(adapter);
4680 /* Do not assert if slot is out of range, just return the advertised
4681 status. testdriver/vbox.py triggers this in logVmInfo. */
4682 if (slot >= mNetworkAdapters.size())
4683 return setError(E_INVALIDARG,
4684 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4685 slot, mNetworkAdapters.size());
4686
4687 AutoCaller autoCaller(this);
4688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4689
4690 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4691
4692 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4693
4694 return S_OK;
4695}
4696
4697STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4698{
4699 CheckComArgOutSafeArrayPointerValid(aKeys);
4700
4701 AutoCaller autoCaller(this);
4702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4703
4704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4705
4706 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4707 int i = 0;
4708 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4709 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4710 ++it, ++i)
4711 {
4712 const Utf8Str &strKey = it->first;
4713 strKey.cloneTo(&saKeys[i]);
4714 }
4715 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4716
4717 return S_OK;
4718 }
4719
4720 /**
4721 * @note Locks this object for reading.
4722 */
4723STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4724 BSTR *aValue)
4725{
4726 CheckComArgStrNotEmptyOrNull(aKey);
4727 CheckComArgOutPointerValid(aValue);
4728
4729 AutoCaller autoCaller(this);
4730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4731
4732 /* start with nothing found */
4733 Bstr bstrResult("");
4734
4735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4736
4737 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4738 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4739 // found:
4740 bstrResult = it->second; // source is a Utf8Str
4741
4742 /* return the result to caller (may be empty) */
4743 bstrResult.cloneTo(aValue);
4744
4745 return S_OK;
4746}
4747
4748 /**
4749 * @note Locks mParent for writing + this object for writing.
4750 */
4751STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4752{
4753 CheckComArgStrNotEmptyOrNull(aKey);
4754
4755 AutoCaller autoCaller(this);
4756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4757
4758 Utf8Str strKey(aKey);
4759 Utf8Str strValue(aValue);
4760 Utf8Str strOldValue; // empty
4761
4762 // locking note: we only hold the read lock briefly to look up the old value,
4763 // then release it and call the onExtraCanChange callbacks. There is a small
4764 // chance of a race insofar as the callback might be called twice if two callers
4765 // change the same key at the same time, but that's a much better solution
4766 // than the deadlock we had here before. The actual changing of the extradata
4767 // is then performed under the write lock and race-free.
4768
4769 // look up the old value first; if nothing has changed then we need not do anything
4770 {
4771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4772 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4773 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4774 strOldValue = it->second;
4775 }
4776
4777 bool fChanged;
4778 if ((fChanged = (strOldValue != strValue)))
4779 {
4780 // ask for permission from all listeners outside the locks;
4781 // onExtraDataCanChange() only briefly requests the VirtualBox
4782 // lock to copy the list of callbacks to invoke
4783 Bstr error;
4784 Bstr bstrValue(aValue);
4785
4786 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4787 {
4788 const char *sep = error.isEmpty() ? "" : ": ";
4789 CBSTR err = error.raw();
4790 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4791 sep, err));
4792 return setError(E_ACCESSDENIED,
4793 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4794 aKey,
4795 bstrValue.raw(),
4796 sep,
4797 err);
4798 }
4799
4800 // data is changing and change not vetoed: then write it out under the lock
4801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4802
4803 if (isSnapshotMachine())
4804 {
4805 HRESULT rc = checkStateDependency(MutableStateDep);
4806 if (FAILED(rc)) return rc;
4807 }
4808
4809 if (strValue.isEmpty())
4810 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4811 else
4812 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4813 // creates a new key if needed
4814
4815 bool fNeedsGlobalSaveSettings = false;
4816 saveSettings(&fNeedsGlobalSaveSettings);
4817
4818 if (fNeedsGlobalSaveSettings)
4819 {
4820 // save the global settings; for that we should hold only the VirtualBox lock
4821 alock.release();
4822 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4823 mParent->saveSettings();
4824 }
4825 }
4826
4827 // fire notification outside the lock
4828 if (fChanged)
4829 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4830
4831 return S_OK;
4832}
4833
4834STDMETHODIMP Machine::SaveSettings()
4835{
4836 AutoCaller autoCaller(this);
4837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4838
4839 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4840
4841 /* when there was auto-conversion, we want to save the file even if
4842 * the VM is saved */
4843 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4844 if (FAILED(rc)) return rc;
4845
4846 /* the settings file path may never be null */
4847 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4848
4849 /* save all VM data excluding snapshots */
4850 bool fNeedsGlobalSaveSettings = false;
4851 rc = saveSettings(&fNeedsGlobalSaveSettings);
4852 mlock.release();
4853
4854 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4855 {
4856 // save the global settings; for that we should hold only the VirtualBox lock
4857 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4858 rc = mParent->saveSettings();
4859 }
4860
4861 return rc;
4862}
4863
4864STDMETHODIMP Machine::DiscardSettings()
4865{
4866 AutoCaller autoCaller(this);
4867 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4868
4869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4870
4871 HRESULT rc = checkStateDependency(MutableStateDep);
4872 if (FAILED(rc)) return rc;
4873
4874 /*
4875 * during this rollback, the session will be notified if data has
4876 * been actually changed
4877 */
4878 rollback(true /* aNotify */);
4879
4880 return S_OK;
4881}
4882
4883/** @note Locks objects! */
4884STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4885 ComSafeArrayOut(IMedium*, aMedia))
4886{
4887 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4888 AutoLimitedCaller autoCaller(this);
4889 AssertComRCReturnRC(autoCaller.rc());
4890
4891 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4892
4893 Guid id(getId());
4894
4895 if (mData->mSession.mState != SessionState_Unlocked)
4896 return setError(VBOX_E_INVALID_OBJECT_STATE,
4897 tr("Cannot unregister the machine '%s' while it is locked"),
4898 mUserData->s.strName.c_str());
4899
4900 // wait for state dependents to drop to zero
4901 ensureNoStateDependencies();
4902
4903 if (!mData->mAccessible)
4904 {
4905 // inaccessible maschines can only be unregistered; uninitialize ourselves
4906 // here because currently there may be no unregistered that are inaccessible
4907 // (this state combination is not supported). Note releasing the caller and
4908 // leaving the lock before calling uninit()
4909 alock.release();
4910 autoCaller.release();
4911
4912 uninit();
4913
4914 mParent->unregisterMachine(this, id);
4915 // calls VirtualBox::saveSettings()
4916
4917 return S_OK;
4918 }
4919
4920 HRESULT rc = S_OK;
4921
4922 // discard saved state
4923 if (mData->mMachineState == MachineState_Saved)
4924 {
4925 // add the saved state file to the list of files the caller should delete
4926 Assert(!mSSData->strStateFilePath.isEmpty());
4927 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4928
4929 mSSData->strStateFilePath.setNull();
4930
4931 // unconditionally set the machine state to powered off, we now
4932 // know no session has locked the machine
4933 mData->mMachineState = MachineState_PoweredOff;
4934 }
4935
4936 size_t cSnapshots = 0;
4937 if (mData->mFirstSnapshot)
4938 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4939 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4940 // fail now before we start detaching media
4941 return setError(VBOX_E_INVALID_OBJECT_STATE,
4942 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4943 mUserData->s.strName.c_str(), cSnapshots);
4944
4945 // This list collects the medium objects from all medium attachments
4946 // which we will detach from the machine and its snapshots, in a specific
4947 // order which allows for closing all media without getting "media in use"
4948 // errors, simply by going through the list from the front to the back:
4949 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4950 // and must be closed before the parent media from the snapshots, or closing the parents
4951 // will fail because they still have children);
4952 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4953 // the root ("first") snapshot of the machine.
4954 MediaList llMedia;
4955
4956 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4957 && mMediaData->mAttachments.size()
4958 )
4959 {
4960 // we have media attachments: detach them all and add the Medium objects to our list
4961 if (cleanupMode != CleanupMode_UnregisterOnly)
4962 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4963 else
4964 return setError(VBOX_E_INVALID_OBJECT_STATE,
4965 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4966 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4967 }
4968
4969 if (cSnapshots)
4970 {
4971 // autoCleanup must be true here, or we would have failed above
4972
4973 // add the media from the medium attachments of the snapshots to llMedia
4974 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4975 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4976 // into the children first
4977
4978 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4979 MachineState_T oldState = mData->mMachineState;
4980 mData->mMachineState = MachineState_DeletingSnapshot;
4981
4982 // make a copy of the first snapshot so the refcount does not drop to 0
4983 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4984 // because of the AutoCaller voodoo)
4985 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4986
4987 // GO!
4988 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4989
4990 mData->mMachineState = oldState;
4991 }
4992
4993 if (FAILED(rc))
4994 {
4995 rollbackMedia();
4996 return rc;
4997 }
4998
4999 // commit all the media changes made above
5000 commitMedia();
5001
5002 mData->mRegistered = false;
5003
5004 // machine lock no longer needed
5005 alock.release();
5006
5007 // return media to caller
5008 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5009 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5010
5011 mParent->unregisterMachine(this, id);
5012 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5013
5014 return S_OK;
5015}
5016
5017struct Machine::DeleteTask
5018{
5019 ComObjPtr<Machine> pMachine;
5020 RTCList<ComPtr<IMedium> > llMediums;
5021 StringsList llFilesToDelete;
5022 ComObjPtr<Progress> pProgress;
5023};
5024
5025STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5026{
5027 LogFlowFuncEnter();
5028
5029 AutoCaller autoCaller(this);
5030 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5031
5032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5033
5034 HRESULT rc = checkStateDependency(MutableStateDep);
5035 if (FAILED(rc)) return rc;
5036
5037 if (mData->mRegistered)
5038 return setError(VBOX_E_INVALID_VM_STATE,
5039 tr("Cannot delete settings of a registered machine"));
5040
5041 DeleteTask *pTask = new DeleteTask;
5042 pTask->pMachine = this;
5043 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5044
5045 // collect files to delete
5046 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5047
5048 for (size_t i = 0; i < sfaMedia.size(); ++i)
5049 {
5050 IMedium *pIMedium(sfaMedia[i]);
5051 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5052 if (pMedium.isNull())
5053 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5054 SafeArray<BSTR> ids;
5055 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5056 if (FAILED(rc)) return rc;
5057 /* At this point the medium should not have any back references
5058 * anymore. If it has it is attached to another VM and *must* not
5059 * deleted. */
5060 if (ids.size() < 1)
5061 pTask->llMediums.append(pMedium);
5062 }
5063 if (mData->pMachineConfigFile->fileExists())
5064 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5065
5066 pTask->pProgress.createObject();
5067 pTask->pProgress->init(getVirtualBox(),
5068 static_cast<IMachine*>(this) /* aInitiator */,
5069 Bstr(tr("Deleting files")).raw(),
5070 true /* fCancellable */,
5071 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5072 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5073
5074 int vrc = RTThreadCreate(NULL,
5075 Machine::deleteThread,
5076 (void*)pTask,
5077 0,
5078 RTTHREADTYPE_MAIN_WORKER,
5079 0,
5080 "MachineDelete");
5081
5082 pTask->pProgress.queryInterfaceTo(aProgress);
5083
5084 if (RT_FAILURE(vrc))
5085 {
5086 delete pTask;
5087 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5088 }
5089
5090 LogFlowFuncLeave();
5091
5092 return S_OK;
5093}
5094
5095/**
5096 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5097 * calls Machine::deleteTaskWorker() on the actual machine object.
5098 * @param Thread
5099 * @param pvUser
5100 * @return
5101 */
5102/*static*/
5103DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5104{
5105 LogFlowFuncEnter();
5106
5107 DeleteTask *pTask = (DeleteTask*)pvUser;
5108 Assert(pTask);
5109 Assert(pTask->pMachine);
5110 Assert(pTask->pProgress);
5111
5112 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5113 pTask->pProgress->notifyComplete(rc);
5114
5115 delete pTask;
5116
5117 LogFlowFuncLeave();
5118
5119 NOREF(Thread);
5120
5121 return VINF_SUCCESS;
5122}
5123
5124/**
5125 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5126 * @param task
5127 * @return
5128 */
5129HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5130{
5131 AutoCaller autoCaller(this);
5132 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5133
5134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5135
5136 HRESULT rc = S_OK;
5137
5138 try
5139 {
5140 ULONG uLogHistoryCount = 3;
5141 ComPtr<ISystemProperties> systemProperties;
5142 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5143 if (FAILED(rc)) throw rc;
5144
5145 if (!systemProperties.isNull())
5146 {
5147 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5148 if (FAILED(rc)) throw rc;
5149 }
5150
5151 MachineState_T oldState = mData->mMachineState;
5152 setMachineState(MachineState_SettingUp);
5153 alock.release();
5154 for (size_t i = 0; i < task.llMediums.size(); ++i)
5155 {
5156 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5157 {
5158 AutoCaller mac(pMedium);
5159 if (FAILED(mac.rc())) throw mac.rc();
5160 Utf8Str strLocation = pMedium->getLocationFull();
5161 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5162 if (FAILED(rc)) throw rc;
5163 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5164 }
5165 ComPtr<IProgress> pProgress2;
5166 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5167 if (FAILED(rc)) throw rc;
5168 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5169 if (FAILED(rc)) throw rc;
5170 /* Check the result of the asynchrony process. */
5171 LONG iRc;
5172 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5173 if (FAILED(rc)) throw rc;
5174 /* If the thread of the progress object has an error, then
5175 * retrieve the error info from there, or it'll be lost. */
5176 if (FAILED(iRc))
5177 throw setError(ProgressErrorInfo(pProgress2));
5178 }
5179 setMachineState(oldState);
5180 alock.acquire();
5181
5182 // delete the files pushed on the task list by Machine::Delete()
5183 // (this includes saved states of the machine and snapshots and
5184 // medium storage files from the IMedium list passed in, and the
5185 // machine XML file)
5186 StringsList::const_iterator it = task.llFilesToDelete.begin();
5187 while (it != task.llFilesToDelete.end())
5188 {
5189 const Utf8Str &strFile = *it;
5190 LogFunc(("Deleting file %s\n", strFile.c_str()));
5191 int vrc = RTFileDelete(strFile.c_str());
5192 if (RT_FAILURE(vrc))
5193 throw setError(VBOX_E_IPRT_ERROR,
5194 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5195
5196 ++it;
5197 if (it == task.llFilesToDelete.end())
5198 {
5199 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5200 if (FAILED(rc)) throw rc;
5201 break;
5202 }
5203
5204 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5205 if (FAILED(rc)) throw rc;
5206 }
5207
5208 /* delete the settings only when the file actually exists */
5209 if (mData->pMachineConfigFile->fileExists())
5210 {
5211 /* Delete any backup or uncommitted XML files. Ignore failures.
5212 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5213 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5214 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5215 RTFileDelete(otherXml.c_str());
5216 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5217 RTFileDelete(otherXml.c_str());
5218
5219 /* delete the Logs folder, nothing important should be left
5220 * there (we don't check for errors because the user might have
5221 * some private files there that we don't want to delete) */
5222 Utf8Str logFolder;
5223 getLogFolder(logFolder);
5224 Assert(logFolder.length());
5225 if (RTDirExists(logFolder.c_str()))
5226 {
5227 /* Delete all VBox.log[.N] files from the Logs folder
5228 * (this must be in sync with the rotation logic in
5229 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5230 * files that may have been created by the GUI. */
5231 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5232 logFolder.c_str(), RTPATH_DELIMITER);
5233 RTFileDelete(log.c_str());
5234 log = Utf8StrFmt("%s%cVBox.png",
5235 logFolder.c_str(), RTPATH_DELIMITER);
5236 RTFileDelete(log.c_str());
5237 for (int i = uLogHistoryCount; i > 0; i--)
5238 {
5239 log = Utf8StrFmt("%s%cVBox.log.%d",
5240 logFolder.c_str(), RTPATH_DELIMITER, i);
5241 RTFileDelete(log.c_str());
5242 log = Utf8StrFmt("%s%cVBox.png.%d",
5243 logFolder.c_str(), RTPATH_DELIMITER, i);
5244 RTFileDelete(log.c_str());
5245 }
5246
5247 RTDirRemove(logFolder.c_str());
5248 }
5249
5250 /* delete the Snapshots folder, nothing important should be left
5251 * there (we don't check for errors because the user might have
5252 * some private files there that we don't want to delete) */
5253 Utf8Str strFullSnapshotFolder;
5254 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5255 Assert(!strFullSnapshotFolder.isEmpty());
5256 if (RTDirExists(strFullSnapshotFolder.c_str()))
5257 RTDirRemove(strFullSnapshotFolder.c_str());
5258
5259 // delete the directory that contains the settings file, but only
5260 // if it matches the VM name
5261 Utf8Str settingsDir;
5262 if (isInOwnDir(&settingsDir))
5263 RTDirRemove(settingsDir.c_str());
5264 }
5265
5266 alock.release();
5267
5268 mParent->saveModifiedRegistries();
5269 }
5270 catch (HRESULT aRC) { rc = aRC; }
5271
5272 return rc;
5273}
5274
5275STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5276{
5277 CheckComArgOutPointerValid(aSnapshot);
5278
5279 AutoCaller autoCaller(this);
5280 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5281
5282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5283
5284 ComObjPtr<Snapshot> pSnapshot;
5285 HRESULT rc;
5286
5287 if (!aNameOrId || !*aNameOrId)
5288 // null case (caller wants root snapshot): findSnapshotById() handles this
5289 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5290 else
5291 {
5292 Guid uuid(aNameOrId);
5293 if (!uuid.isEmpty())
5294 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5295 else
5296 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5297 }
5298 pSnapshot.queryInterfaceTo(aSnapshot);
5299
5300 return rc;
5301}
5302
5303STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5304{
5305 CheckComArgStrNotEmptyOrNull(aName);
5306 CheckComArgStrNotEmptyOrNull(aHostPath);
5307
5308 AutoCaller autoCaller(this);
5309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5310
5311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5312
5313 HRESULT rc = checkStateDependency(MutableStateDep);
5314 if (FAILED(rc)) return rc;
5315
5316 Utf8Str strName(aName);
5317
5318 ComObjPtr<SharedFolder> sharedFolder;
5319 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5320 if (SUCCEEDED(rc))
5321 return setError(VBOX_E_OBJECT_IN_USE,
5322 tr("Shared folder named '%s' already exists"),
5323 strName.c_str());
5324
5325 sharedFolder.createObject();
5326 rc = sharedFolder->init(getMachine(),
5327 strName,
5328 aHostPath,
5329 !!aWritable,
5330 !!aAutoMount,
5331 true /* fFailOnError */);
5332 if (FAILED(rc)) return rc;
5333
5334 setModified(IsModified_SharedFolders);
5335 mHWData.backup();
5336 mHWData->mSharedFolders.push_back(sharedFolder);
5337
5338 /* inform the direct session if any */
5339 alock.release();
5340 onSharedFolderChange();
5341
5342 return S_OK;
5343}
5344
5345STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5346{
5347 CheckComArgStrNotEmptyOrNull(aName);
5348
5349 AutoCaller autoCaller(this);
5350 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5351
5352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5353
5354 HRESULT rc = checkStateDependency(MutableStateDep);
5355 if (FAILED(rc)) return rc;
5356
5357 ComObjPtr<SharedFolder> sharedFolder;
5358 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5359 if (FAILED(rc)) return rc;
5360
5361 setModified(IsModified_SharedFolders);
5362 mHWData.backup();
5363 mHWData->mSharedFolders.remove(sharedFolder);
5364
5365 /* inform the direct session if any */
5366 alock.release();
5367 onSharedFolderChange();
5368
5369 return S_OK;
5370}
5371
5372STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5373{
5374 CheckComArgOutPointerValid(aCanShow);
5375
5376 /* start with No */
5377 *aCanShow = FALSE;
5378
5379 AutoCaller autoCaller(this);
5380 AssertComRCReturnRC(autoCaller.rc());
5381
5382 ComPtr<IInternalSessionControl> directControl;
5383 {
5384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5385
5386 if (mData->mSession.mState != SessionState_Locked)
5387 return setError(VBOX_E_INVALID_VM_STATE,
5388 tr("Machine is not locked for session (session state: %s)"),
5389 Global::stringifySessionState(mData->mSession.mState));
5390
5391 directControl = mData->mSession.mDirectControl;
5392 }
5393
5394 /* ignore calls made after #OnSessionEnd() is called */
5395 if (!directControl)
5396 return S_OK;
5397
5398 LONG64 dummy;
5399 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5400}
5401
5402STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5403{
5404 CheckComArgOutPointerValid(aWinId);
5405
5406 AutoCaller autoCaller(this);
5407 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5408
5409 ComPtr<IInternalSessionControl> directControl;
5410 {
5411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5412
5413 if (mData->mSession.mState != SessionState_Locked)
5414 return setError(E_FAIL,
5415 tr("Machine is not locked for session (session state: %s)"),
5416 Global::stringifySessionState(mData->mSession.mState));
5417
5418 directControl = mData->mSession.mDirectControl;
5419 }
5420
5421 /* ignore calls made after #OnSessionEnd() is called */
5422 if (!directControl)
5423 return S_OK;
5424
5425 BOOL dummy;
5426 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5427}
5428
5429#ifdef VBOX_WITH_GUEST_PROPS
5430/**
5431 * Look up a guest property in VBoxSVC's internal structures.
5432 */
5433HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5434 BSTR *aValue,
5435 LONG64 *aTimestamp,
5436 BSTR *aFlags) const
5437{
5438 using namespace guestProp;
5439
5440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5441 Utf8Str strName(aName);
5442 HWData::GuestPropertyList::const_iterator it;
5443
5444 for (it = mHWData->mGuestProperties.begin();
5445 it != mHWData->mGuestProperties.end(); ++it)
5446 {
5447 if (it->strName == strName)
5448 {
5449 char szFlags[MAX_FLAGS_LEN + 1];
5450 it->strValue.cloneTo(aValue);
5451 *aTimestamp = it->mTimestamp;
5452 writeFlags(it->mFlags, szFlags);
5453 Bstr(szFlags).cloneTo(aFlags);
5454 break;
5455 }
5456 }
5457 return S_OK;
5458}
5459
5460/**
5461 * Query the VM that a guest property belongs to for the property.
5462 * @returns E_ACCESSDENIED if the VM process is not available or not
5463 * currently handling queries and the lookup should then be done in
5464 * VBoxSVC.
5465 */
5466HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5467 BSTR *aValue,
5468 LONG64 *aTimestamp,
5469 BSTR *aFlags) const
5470{
5471 HRESULT rc;
5472 ComPtr<IInternalSessionControl> directControl;
5473 directControl = mData->mSession.mDirectControl;
5474
5475 /* fail if we were called after #OnSessionEnd() is called. This is a
5476 * silly race condition. */
5477
5478 if (!directControl)
5479 rc = E_ACCESSDENIED;
5480 else
5481 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5482 false /* isSetter */,
5483 aValue, aTimestamp, aFlags);
5484 return rc;
5485}
5486#endif // VBOX_WITH_GUEST_PROPS
5487
5488STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5489 BSTR *aValue,
5490 LONG64 *aTimestamp,
5491 BSTR *aFlags)
5492{
5493#ifndef VBOX_WITH_GUEST_PROPS
5494 ReturnComNotImplemented();
5495#else // VBOX_WITH_GUEST_PROPS
5496 CheckComArgStrNotEmptyOrNull(aName);
5497 CheckComArgOutPointerValid(aValue);
5498 CheckComArgOutPointerValid(aTimestamp);
5499 CheckComArgOutPointerValid(aFlags);
5500
5501 AutoCaller autoCaller(this);
5502 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5503
5504 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5505 if (rc == E_ACCESSDENIED)
5506 /* The VM is not running or the service is not (yet) accessible */
5507 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5508 return rc;
5509#endif // VBOX_WITH_GUEST_PROPS
5510}
5511
5512STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5513{
5514 LONG64 dummyTimestamp;
5515 Bstr dummyFlags;
5516 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5517}
5518
5519STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5520{
5521 Bstr dummyValue;
5522 Bstr dummyFlags;
5523 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5524}
5525
5526#ifdef VBOX_WITH_GUEST_PROPS
5527/**
5528 * Set a guest property in VBoxSVC's internal structures.
5529 */
5530HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5531 IN_BSTR aFlags)
5532{
5533 using namespace guestProp;
5534
5535 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5536 HRESULT rc = S_OK;
5537 HWData::GuestProperty property;
5538 property.mFlags = NILFLAG;
5539 bool found = false;
5540
5541 rc = checkStateDependency(MutableStateDep);
5542 if (FAILED(rc)) return rc;
5543
5544 try
5545 {
5546 Utf8Str utf8Name(aName);
5547 Utf8Str utf8Flags(aFlags);
5548 uint32_t fFlags = NILFLAG;
5549 if ( (aFlags != NULL)
5550 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5551 )
5552 return setError(E_INVALIDARG,
5553 tr("Invalid flag values: '%ls'"),
5554 aFlags);
5555
5556 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5557 * know, this is simple and do an OK job atm.) */
5558 HWData::GuestPropertyList::iterator it;
5559 for (it = mHWData->mGuestProperties.begin();
5560 it != mHWData->mGuestProperties.end(); ++it)
5561 if (it->strName == utf8Name)
5562 {
5563 property = *it;
5564 if (it->mFlags & (RDONLYHOST))
5565 rc = setError(E_ACCESSDENIED,
5566 tr("The property '%ls' cannot be changed by the host"),
5567 aName);
5568 else
5569 {
5570 setModified(IsModified_MachineData);
5571 mHWData.backup(); // @todo r=dj backup in a loop?!?
5572
5573 /* The backup() operation invalidates our iterator, so
5574 * get a new one. */
5575 for (it = mHWData->mGuestProperties.begin();
5576 it->strName != utf8Name;
5577 ++it)
5578 ;
5579 mHWData->mGuestProperties.erase(it);
5580 }
5581 found = true;
5582 break;
5583 }
5584 if (found && SUCCEEDED(rc))
5585 {
5586 if (aValue)
5587 {
5588 RTTIMESPEC time;
5589 property.strValue = aValue;
5590 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5591 if (aFlags != NULL)
5592 property.mFlags = fFlags;
5593 mHWData->mGuestProperties.push_back(property);
5594 }
5595 }
5596 else if (SUCCEEDED(rc) && aValue)
5597 {
5598 RTTIMESPEC time;
5599 setModified(IsModified_MachineData);
5600 mHWData.backup();
5601 property.strName = aName;
5602 property.strValue = aValue;
5603 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5604 property.mFlags = fFlags;
5605 mHWData->mGuestProperties.push_back(property);
5606 }
5607 if ( SUCCEEDED(rc)
5608 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5609 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5610 RTSTR_MAX,
5611 utf8Name.c_str(),
5612 RTSTR_MAX,
5613 NULL)
5614 )
5615 )
5616 {
5617 /** @todo r=bird: Why aren't we leaving the lock here? The
5618 * same code in PushGuestProperty does... */
5619 mParent->onGuestPropertyChange(mData->mUuid, aName,
5620 aValue ? aValue : Bstr("").raw(),
5621 aFlags ? aFlags : Bstr("").raw());
5622 }
5623 }
5624 catch (std::bad_alloc &)
5625 {
5626 rc = E_OUTOFMEMORY;
5627 }
5628
5629 return rc;
5630}
5631
5632/**
5633 * Set a property on the VM that that property belongs to.
5634 * @returns E_ACCESSDENIED if the VM process is not available or not
5635 * currently handling queries and the setting should then be done in
5636 * VBoxSVC.
5637 */
5638HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5639 IN_BSTR aFlags)
5640{
5641 HRESULT rc;
5642
5643 try
5644 {
5645 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5646
5647 BSTR dummy = NULL; /* will not be changed (setter) */
5648 LONG64 dummy64;
5649 if (!directControl)
5650 rc = E_ACCESSDENIED;
5651 else
5652 /** @todo Fix when adding DeleteGuestProperty(),
5653 see defect. */
5654 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5655 true /* isSetter */,
5656 &dummy, &dummy64, &dummy);
5657 }
5658 catch (std::bad_alloc &)
5659 {
5660 rc = E_OUTOFMEMORY;
5661 }
5662
5663 return rc;
5664}
5665#endif // VBOX_WITH_GUEST_PROPS
5666
5667STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5668 IN_BSTR aFlags)
5669{
5670#ifndef VBOX_WITH_GUEST_PROPS
5671 ReturnComNotImplemented();
5672#else // VBOX_WITH_GUEST_PROPS
5673 CheckComArgStrNotEmptyOrNull(aName);
5674 CheckComArgMaybeNull(aFlags);
5675 CheckComArgMaybeNull(aValue);
5676
5677 AutoCaller autoCaller(this);
5678 if (FAILED(autoCaller.rc()))
5679 return autoCaller.rc();
5680
5681 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5682 if (rc == E_ACCESSDENIED)
5683 /* The VM is not running or the service is not (yet) accessible */
5684 rc = setGuestPropertyToService(aName, aValue, aFlags);
5685 return rc;
5686#endif // VBOX_WITH_GUEST_PROPS
5687}
5688
5689STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5690{
5691 return SetGuestProperty(aName, aValue, NULL);
5692}
5693
5694STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5695{
5696 return SetGuestProperty(aName, NULL, NULL);
5697}
5698
5699#ifdef VBOX_WITH_GUEST_PROPS
5700/**
5701 * Enumerate the guest properties in VBoxSVC's internal structures.
5702 */
5703HRESULT Machine::enumerateGuestPropertiesInService
5704 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5705 ComSafeArrayOut(BSTR, aValues),
5706 ComSafeArrayOut(LONG64, aTimestamps),
5707 ComSafeArrayOut(BSTR, aFlags))
5708{
5709 using namespace guestProp;
5710
5711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5712 Utf8Str strPatterns(aPatterns);
5713
5714 /*
5715 * Look for matching patterns and build up a list.
5716 */
5717 HWData::GuestPropertyList propList;
5718 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5719 it != mHWData->mGuestProperties.end();
5720 ++it)
5721 if ( strPatterns.isEmpty()
5722 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5723 RTSTR_MAX,
5724 it->strName.c_str(),
5725 RTSTR_MAX,
5726 NULL)
5727 )
5728 propList.push_back(*it);
5729
5730 /*
5731 * And build up the arrays for returning the property information.
5732 */
5733 size_t cEntries = propList.size();
5734 SafeArray<BSTR> names(cEntries);
5735 SafeArray<BSTR> values(cEntries);
5736 SafeArray<LONG64> timestamps(cEntries);
5737 SafeArray<BSTR> flags(cEntries);
5738 size_t iProp = 0;
5739 for (HWData::GuestPropertyList::iterator it = propList.begin();
5740 it != propList.end();
5741 ++it)
5742 {
5743 char szFlags[MAX_FLAGS_LEN + 1];
5744 it->strName.cloneTo(&names[iProp]);
5745 it->strValue.cloneTo(&values[iProp]);
5746 timestamps[iProp] = it->mTimestamp;
5747 writeFlags(it->mFlags, szFlags);
5748 Bstr(szFlags).cloneTo(&flags[iProp]);
5749 ++iProp;
5750 }
5751 names.detachTo(ComSafeArrayOutArg(aNames));
5752 values.detachTo(ComSafeArrayOutArg(aValues));
5753 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5754 flags.detachTo(ComSafeArrayOutArg(aFlags));
5755 return S_OK;
5756}
5757
5758/**
5759 * Enumerate the properties managed by a VM.
5760 * @returns E_ACCESSDENIED if the VM process is not available or not
5761 * currently handling queries and the setting should then be done in
5762 * VBoxSVC.
5763 */
5764HRESULT Machine::enumerateGuestPropertiesOnVM
5765 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5766 ComSafeArrayOut(BSTR, aValues),
5767 ComSafeArrayOut(LONG64, aTimestamps),
5768 ComSafeArrayOut(BSTR, aFlags))
5769{
5770 HRESULT rc;
5771 ComPtr<IInternalSessionControl> directControl;
5772 directControl = mData->mSession.mDirectControl;
5773
5774 if (!directControl)
5775 rc = E_ACCESSDENIED;
5776 else
5777 rc = directControl->EnumerateGuestProperties
5778 (aPatterns, ComSafeArrayOutArg(aNames),
5779 ComSafeArrayOutArg(aValues),
5780 ComSafeArrayOutArg(aTimestamps),
5781 ComSafeArrayOutArg(aFlags));
5782 return rc;
5783}
5784#endif // VBOX_WITH_GUEST_PROPS
5785
5786STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5787 ComSafeArrayOut(BSTR, aNames),
5788 ComSafeArrayOut(BSTR, aValues),
5789 ComSafeArrayOut(LONG64, aTimestamps),
5790 ComSafeArrayOut(BSTR, aFlags))
5791{
5792#ifndef VBOX_WITH_GUEST_PROPS
5793 ReturnComNotImplemented();
5794#else // VBOX_WITH_GUEST_PROPS
5795 CheckComArgMaybeNull(aPatterns);
5796 CheckComArgOutSafeArrayPointerValid(aNames);
5797 CheckComArgOutSafeArrayPointerValid(aValues);
5798 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5799 CheckComArgOutSafeArrayPointerValid(aFlags);
5800
5801 AutoCaller autoCaller(this);
5802 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5803
5804 HRESULT rc = enumerateGuestPropertiesOnVM
5805 (aPatterns, ComSafeArrayOutArg(aNames),
5806 ComSafeArrayOutArg(aValues),
5807 ComSafeArrayOutArg(aTimestamps),
5808 ComSafeArrayOutArg(aFlags));
5809 if (rc == E_ACCESSDENIED)
5810 /* The VM is not running or the service is not (yet) accessible */
5811 rc = enumerateGuestPropertiesInService
5812 (aPatterns, ComSafeArrayOutArg(aNames),
5813 ComSafeArrayOutArg(aValues),
5814 ComSafeArrayOutArg(aTimestamps),
5815 ComSafeArrayOutArg(aFlags));
5816 return rc;
5817#endif // VBOX_WITH_GUEST_PROPS
5818}
5819
5820STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5821 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5822{
5823 MediaData::AttachmentList atts;
5824
5825 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5826 if (FAILED(rc)) return rc;
5827
5828 SafeIfaceArray<IMediumAttachment> attachments(atts);
5829 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5830
5831 return S_OK;
5832}
5833
5834STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5835 LONG aControllerPort,
5836 LONG aDevice,
5837 IMediumAttachment **aAttachment)
5838{
5839 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5840 aControllerName, aControllerPort, aDevice));
5841
5842 CheckComArgStrNotEmptyOrNull(aControllerName);
5843 CheckComArgOutPointerValid(aAttachment);
5844
5845 AutoCaller autoCaller(this);
5846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5847
5848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5849
5850 *aAttachment = NULL;
5851
5852 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5853 aControllerName,
5854 aControllerPort,
5855 aDevice);
5856 if (pAttach.isNull())
5857 return setError(VBOX_E_OBJECT_NOT_FOUND,
5858 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5859 aDevice, aControllerPort, aControllerName);
5860
5861 pAttach.queryInterfaceTo(aAttachment);
5862
5863 return S_OK;
5864}
5865
5866STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5867 StorageBus_T aConnectionType,
5868 IStorageController **controller)
5869{
5870 CheckComArgStrNotEmptyOrNull(aName);
5871
5872 if ( (aConnectionType <= StorageBus_Null)
5873 || (aConnectionType > StorageBus_SAS))
5874 return setError(E_INVALIDARG,
5875 tr("Invalid connection type: %d"),
5876 aConnectionType);
5877
5878 AutoCaller autoCaller(this);
5879 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5880
5881 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5882
5883 HRESULT rc = checkStateDependency(MutableStateDep);
5884 if (FAILED(rc)) return rc;
5885
5886 /* try to find one with the name first. */
5887 ComObjPtr<StorageController> ctrl;
5888
5889 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5890 if (SUCCEEDED(rc))
5891 return setError(VBOX_E_OBJECT_IN_USE,
5892 tr("Storage controller named '%ls' already exists"),
5893 aName);
5894
5895 ctrl.createObject();
5896
5897 /* get a new instance number for the storage controller */
5898 ULONG ulInstance = 0;
5899 bool fBootable = true;
5900 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5901 it != mStorageControllers->end();
5902 ++it)
5903 {
5904 if ((*it)->getStorageBus() == aConnectionType)
5905 {
5906 ULONG ulCurInst = (*it)->getInstance();
5907
5908 if (ulCurInst >= ulInstance)
5909 ulInstance = ulCurInst + 1;
5910
5911 /* Only one controller of each type can be marked as bootable. */
5912 if ((*it)->getBootable())
5913 fBootable = false;
5914 }
5915 }
5916
5917 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5918 if (FAILED(rc)) return rc;
5919
5920 setModified(IsModified_Storage);
5921 mStorageControllers.backup();
5922 mStorageControllers->push_back(ctrl);
5923
5924 ctrl.queryInterfaceTo(controller);
5925
5926 /* inform the direct session if any */
5927 alock.release();
5928 onStorageControllerChange();
5929
5930 return S_OK;
5931}
5932
5933STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5934 IStorageController **aStorageController)
5935{
5936 CheckComArgStrNotEmptyOrNull(aName);
5937
5938 AutoCaller autoCaller(this);
5939 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5940
5941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5942
5943 ComObjPtr<StorageController> ctrl;
5944
5945 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5946 if (SUCCEEDED(rc))
5947 ctrl.queryInterfaceTo(aStorageController);
5948
5949 return rc;
5950}
5951
5952STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5953 IStorageController **aStorageController)
5954{
5955 AutoCaller autoCaller(this);
5956 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5957
5958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5959
5960 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5961 it != mStorageControllers->end();
5962 ++it)
5963 {
5964 if ((*it)->getInstance() == aInstance)
5965 {
5966 (*it).queryInterfaceTo(aStorageController);
5967 return S_OK;
5968 }
5969 }
5970
5971 return setError(VBOX_E_OBJECT_NOT_FOUND,
5972 tr("Could not find a storage controller with instance number '%lu'"),
5973 aInstance);
5974}
5975
5976STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5977{
5978 AutoCaller autoCaller(this);
5979 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5980
5981 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5982
5983 HRESULT rc = checkStateDependency(MutableStateDep);
5984 if (FAILED(rc)) return rc;
5985
5986 ComObjPtr<StorageController> ctrl;
5987
5988 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5989 if (SUCCEEDED(rc))
5990 {
5991 /* Ensure that only one controller of each type is marked as bootable. */
5992 if (fBootable == TRUE)
5993 {
5994 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5995 it != mStorageControllers->end();
5996 ++it)
5997 {
5998 ComObjPtr<StorageController> aCtrl = (*it);
5999
6000 if ( (aCtrl->getName() != Utf8Str(aName))
6001 && aCtrl->getBootable() == TRUE
6002 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6003 && aCtrl->getControllerType() == ctrl->getControllerType())
6004 {
6005 aCtrl->setBootable(FALSE);
6006 break;
6007 }
6008 }
6009 }
6010
6011 if (SUCCEEDED(rc))
6012 {
6013 ctrl->setBootable(fBootable);
6014 setModified(IsModified_Storage);
6015 }
6016 }
6017
6018 if (SUCCEEDED(rc))
6019 {
6020 /* inform the direct session if any */
6021 alock.release();
6022 onStorageControllerChange();
6023 }
6024
6025 return rc;
6026}
6027
6028STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6029{
6030 CheckComArgStrNotEmptyOrNull(aName);
6031
6032 AutoCaller autoCaller(this);
6033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6034
6035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6036
6037 HRESULT rc = checkStateDependency(MutableStateDep);
6038 if (FAILED(rc)) return rc;
6039
6040 ComObjPtr<StorageController> ctrl;
6041 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6042 if (FAILED(rc)) return rc;
6043
6044 {
6045 /* find all attached devices to the appropriate storage controller and detach them all */
6046 // make a temporary list because detachDevice invalidates iterators into
6047 // mMediaData->mAttachments
6048 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6049
6050 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6051 it != llAttachments2.end();
6052 ++it)
6053 {
6054 MediumAttachment *pAttachTemp = *it;
6055
6056 AutoCaller localAutoCaller(pAttachTemp);
6057 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6058
6059 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6060
6061 if (pAttachTemp->getControllerName() == aName)
6062 {
6063 rc = detachDevice(pAttachTemp, alock, NULL);
6064 if (FAILED(rc)) return rc;
6065 }
6066 }
6067 }
6068
6069 /* We can remove it now. */
6070 setModified(IsModified_Storage);
6071 mStorageControllers.backup();
6072
6073 ctrl->unshare();
6074
6075 mStorageControllers->remove(ctrl);
6076
6077 /* inform the direct session if any */
6078 alock.release();
6079 onStorageControllerChange();
6080
6081 return S_OK;
6082}
6083
6084STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6085 ULONG *puOriginX,
6086 ULONG *puOriginY,
6087 ULONG *puWidth,
6088 ULONG *puHeight,
6089 BOOL *pfEnabled)
6090{
6091 LogFlowThisFunc(("\n"));
6092
6093 CheckComArgNotNull(puOriginX);
6094 CheckComArgNotNull(puOriginY);
6095 CheckComArgNotNull(puWidth);
6096 CheckComArgNotNull(puHeight);
6097 CheckComArgNotNull(pfEnabled);
6098
6099 uint32_t u32OriginX= 0;
6100 uint32_t u32OriginY= 0;
6101 uint32_t u32Width = 0;
6102 uint32_t u32Height = 0;
6103 uint16_t u16Flags = 0;
6104
6105 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6106 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6107 if (RT_FAILURE(vrc))
6108 {
6109#ifdef RT_OS_WINDOWS
6110 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6111 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6112 * So just assign fEnable to TRUE again.
6113 * The right fix would be to change GUI API wrappers to make sure that parameters
6114 * are changed only if API succeeds.
6115 */
6116 *pfEnabled = TRUE;
6117#endif
6118 return setError(VBOX_E_IPRT_ERROR,
6119 tr("Saved guest size is not available (%Rrc)"),
6120 vrc);
6121 }
6122
6123 *puOriginX = u32OriginX;
6124 *puOriginY = u32OriginY;
6125 *puWidth = u32Width;
6126 *puHeight = u32Height;
6127 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6128
6129 return S_OK;
6130}
6131
6132STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6133{
6134 LogFlowThisFunc(("\n"));
6135
6136 CheckComArgNotNull(aSize);
6137 CheckComArgNotNull(aWidth);
6138 CheckComArgNotNull(aHeight);
6139
6140 if (aScreenId != 0)
6141 return E_NOTIMPL;
6142
6143 AutoCaller autoCaller(this);
6144 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6145
6146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6147
6148 uint8_t *pu8Data = NULL;
6149 uint32_t cbData = 0;
6150 uint32_t u32Width = 0;
6151 uint32_t u32Height = 0;
6152
6153 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6154
6155 if (RT_FAILURE(vrc))
6156 return setError(VBOX_E_IPRT_ERROR,
6157 tr("Saved screenshot data is not available (%Rrc)"),
6158 vrc);
6159
6160 *aSize = cbData;
6161 *aWidth = u32Width;
6162 *aHeight = u32Height;
6163
6164 freeSavedDisplayScreenshot(pu8Data);
6165
6166 return S_OK;
6167}
6168
6169STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6170{
6171 LogFlowThisFunc(("\n"));
6172
6173 CheckComArgNotNull(aWidth);
6174 CheckComArgNotNull(aHeight);
6175 CheckComArgOutSafeArrayPointerValid(aData);
6176
6177 if (aScreenId != 0)
6178 return E_NOTIMPL;
6179
6180 AutoCaller autoCaller(this);
6181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6182
6183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6184
6185 uint8_t *pu8Data = NULL;
6186 uint32_t cbData = 0;
6187 uint32_t u32Width = 0;
6188 uint32_t u32Height = 0;
6189
6190 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6191
6192 if (RT_FAILURE(vrc))
6193 return setError(VBOX_E_IPRT_ERROR,
6194 tr("Saved screenshot data is not available (%Rrc)"),
6195 vrc);
6196
6197 *aWidth = u32Width;
6198 *aHeight = u32Height;
6199
6200 com::SafeArray<BYTE> bitmap(cbData);
6201 /* Convert pixels to format expected by the API caller. */
6202 if (aBGR)
6203 {
6204 /* [0] B, [1] G, [2] R, [3] A. */
6205 for (unsigned i = 0; i < cbData; i += 4)
6206 {
6207 bitmap[i] = pu8Data[i];
6208 bitmap[i + 1] = pu8Data[i + 1];
6209 bitmap[i + 2] = pu8Data[i + 2];
6210 bitmap[i + 3] = 0xff;
6211 }
6212 }
6213 else
6214 {
6215 /* [0] R, [1] G, [2] B, [3] A. */
6216 for (unsigned i = 0; i < cbData; i += 4)
6217 {
6218 bitmap[i] = pu8Data[i + 2];
6219 bitmap[i + 1] = pu8Data[i + 1];
6220 bitmap[i + 2] = pu8Data[i];
6221 bitmap[i + 3] = 0xff;
6222 }
6223 }
6224 bitmap.detachTo(ComSafeArrayOutArg(aData));
6225
6226 freeSavedDisplayScreenshot(pu8Data);
6227
6228 return S_OK;
6229}
6230
6231
6232STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6233{
6234 LogFlowThisFunc(("\n"));
6235
6236 CheckComArgNotNull(aWidth);
6237 CheckComArgNotNull(aHeight);
6238 CheckComArgOutSafeArrayPointerValid(aData);
6239
6240 if (aScreenId != 0)
6241 return E_NOTIMPL;
6242
6243 AutoCaller autoCaller(this);
6244 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6245
6246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 uint8_t *pu8Data = NULL;
6249 uint32_t cbData = 0;
6250 uint32_t u32Width = 0;
6251 uint32_t u32Height = 0;
6252
6253 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6254
6255 if (RT_FAILURE(vrc))
6256 return setError(VBOX_E_IPRT_ERROR,
6257 tr("Saved screenshot data is not available (%Rrc)"),
6258 vrc);
6259
6260 *aWidth = u32Width;
6261 *aHeight = u32Height;
6262
6263 HRESULT rc = S_OK;
6264 uint8_t *pu8PNG = NULL;
6265 uint32_t cbPNG = 0;
6266 uint32_t cxPNG = 0;
6267 uint32_t cyPNG = 0;
6268
6269 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6270
6271 if (RT_SUCCESS(vrc))
6272 {
6273 com::SafeArray<BYTE> screenData(cbPNG);
6274 screenData.initFrom(pu8PNG, cbPNG);
6275 if (pu8PNG)
6276 RTMemFree(pu8PNG);
6277 screenData.detachTo(ComSafeArrayOutArg(aData));
6278 }
6279 else
6280 {
6281 if (pu8PNG)
6282 RTMemFree(pu8PNG);
6283 return setError(VBOX_E_IPRT_ERROR,
6284 tr("Could not convert screenshot to PNG (%Rrc)"),
6285 vrc);
6286 }
6287
6288 freeSavedDisplayScreenshot(pu8Data);
6289
6290 return rc;
6291}
6292
6293STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6294{
6295 LogFlowThisFunc(("\n"));
6296
6297 CheckComArgNotNull(aSize);
6298 CheckComArgNotNull(aWidth);
6299 CheckComArgNotNull(aHeight);
6300
6301 if (aScreenId != 0)
6302 return E_NOTIMPL;
6303
6304 AutoCaller autoCaller(this);
6305 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6306
6307 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6308
6309 uint8_t *pu8Data = NULL;
6310 uint32_t cbData = 0;
6311 uint32_t u32Width = 0;
6312 uint32_t u32Height = 0;
6313
6314 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6315
6316 if (RT_FAILURE(vrc))
6317 return setError(VBOX_E_IPRT_ERROR,
6318 tr("Saved screenshot data is not available (%Rrc)"),
6319 vrc);
6320
6321 *aSize = cbData;
6322 *aWidth = u32Width;
6323 *aHeight = u32Height;
6324
6325 freeSavedDisplayScreenshot(pu8Data);
6326
6327 return S_OK;
6328}
6329
6330STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6331{
6332 LogFlowThisFunc(("\n"));
6333
6334 CheckComArgNotNull(aWidth);
6335 CheckComArgNotNull(aHeight);
6336 CheckComArgOutSafeArrayPointerValid(aData);
6337
6338 if (aScreenId != 0)
6339 return E_NOTIMPL;
6340
6341 AutoCaller autoCaller(this);
6342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6343
6344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 uint8_t *pu8Data = NULL;
6347 uint32_t cbData = 0;
6348 uint32_t u32Width = 0;
6349 uint32_t u32Height = 0;
6350
6351 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6352
6353 if (RT_FAILURE(vrc))
6354 return setError(VBOX_E_IPRT_ERROR,
6355 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6356 vrc);
6357
6358 *aWidth = u32Width;
6359 *aHeight = u32Height;
6360
6361 com::SafeArray<BYTE> png(cbData);
6362 png.initFrom(pu8Data, cbData);
6363 png.detachTo(ComSafeArrayOutArg(aData));
6364
6365 freeSavedDisplayScreenshot(pu8Data);
6366
6367 return S_OK;
6368}
6369
6370STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6371{
6372 HRESULT rc = S_OK;
6373 LogFlowThisFunc(("\n"));
6374
6375 AutoCaller autoCaller(this);
6376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6377
6378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6379
6380 if (!mHWData->mCPUHotPlugEnabled)
6381 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6382
6383 if (aCpu >= mHWData->mCPUCount)
6384 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6385
6386 if (mHWData->mCPUAttached[aCpu])
6387 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6388
6389 alock.release();
6390 rc = onCPUChange(aCpu, false);
6391 alock.acquire();
6392 if (FAILED(rc)) return rc;
6393
6394 setModified(IsModified_MachineData);
6395 mHWData.backup();
6396 mHWData->mCPUAttached[aCpu] = true;
6397
6398 /* Save settings if online */
6399 if (Global::IsOnline(mData->mMachineState))
6400 saveSettings(NULL);
6401
6402 return S_OK;
6403}
6404
6405STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6406{
6407 HRESULT rc = S_OK;
6408 LogFlowThisFunc(("\n"));
6409
6410 AutoCaller autoCaller(this);
6411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6412
6413 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 if (!mHWData->mCPUHotPlugEnabled)
6416 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6417
6418 if (aCpu >= SchemaDefs::MaxCPUCount)
6419 return setError(E_INVALIDARG,
6420 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6421 SchemaDefs::MaxCPUCount);
6422
6423 if (!mHWData->mCPUAttached[aCpu])
6424 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6425
6426 /* CPU 0 can't be detached */
6427 if (aCpu == 0)
6428 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6429
6430 alock.release();
6431 rc = onCPUChange(aCpu, true);
6432 alock.acquire();
6433 if (FAILED(rc)) return rc;
6434
6435 setModified(IsModified_MachineData);
6436 mHWData.backup();
6437 mHWData->mCPUAttached[aCpu] = false;
6438
6439 /* Save settings if online */
6440 if (Global::IsOnline(mData->mMachineState))
6441 saveSettings(NULL);
6442
6443 return S_OK;
6444}
6445
6446STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6447{
6448 LogFlowThisFunc(("\n"));
6449
6450 CheckComArgNotNull(aCpuAttached);
6451
6452 *aCpuAttached = false;
6453
6454 AutoCaller autoCaller(this);
6455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6456
6457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6458
6459 /* If hotplug is enabled the CPU is always enabled. */
6460 if (!mHWData->mCPUHotPlugEnabled)
6461 {
6462 if (aCpu < mHWData->mCPUCount)
6463 *aCpuAttached = true;
6464 }
6465 else
6466 {
6467 if (aCpu < SchemaDefs::MaxCPUCount)
6468 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6469 }
6470
6471 return S_OK;
6472}
6473
6474STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6475{
6476 CheckComArgOutPointerValid(aName);
6477
6478 AutoCaller autoCaller(this);
6479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6480
6481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6482
6483 Utf8Str log = queryLogFilename(aIdx);
6484 if (!RTFileExists(log.c_str()))
6485 log.setNull();
6486 log.cloneTo(aName);
6487
6488 return S_OK;
6489}
6490
6491STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6492{
6493 LogFlowThisFunc(("\n"));
6494 CheckComArgOutSafeArrayPointerValid(aData);
6495 if (aSize < 0)
6496 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6497
6498 AutoCaller autoCaller(this);
6499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6500
6501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6502
6503 HRESULT rc = S_OK;
6504 Utf8Str log = queryLogFilename(aIdx);
6505
6506 /* do not unnecessarily hold the lock while doing something which does
6507 * not need the lock and potentially takes a long time. */
6508 alock.release();
6509
6510 /* Limit the chunk size to 32K for now, as that gives better performance
6511 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6512 * One byte expands to approx. 25 bytes of breathtaking XML. */
6513 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6514 com::SafeArray<BYTE> logData(cbData);
6515
6516 RTFILE LogFile;
6517 int vrc = RTFileOpen(&LogFile, log.c_str(),
6518 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6519 if (RT_SUCCESS(vrc))
6520 {
6521 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6522 if (RT_SUCCESS(vrc))
6523 logData.resize(cbData);
6524 else
6525 rc = setError(VBOX_E_IPRT_ERROR,
6526 tr("Could not read log file '%s' (%Rrc)"),
6527 log.c_str(), vrc);
6528 RTFileClose(LogFile);
6529 }
6530 else
6531 rc = setError(VBOX_E_IPRT_ERROR,
6532 tr("Could not open log file '%s' (%Rrc)"),
6533 log.c_str(), vrc);
6534
6535 if (FAILED(rc))
6536 logData.resize(0);
6537 logData.detachTo(ComSafeArrayOutArg(aData));
6538
6539 return rc;
6540}
6541
6542
6543/**
6544 * Currently this method doesn't attach device to the running VM,
6545 * just makes sure it's plugged on next VM start.
6546 */
6547STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6548{
6549 AutoCaller autoCaller(this);
6550 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6551
6552 // lock scope
6553 {
6554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6555
6556 HRESULT rc = checkStateDependency(MutableStateDep);
6557 if (FAILED(rc)) return rc;
6558
6559 ChipsetType_T aChipset = ChipsetType_PIIX3;
6560 COMGETTER(ChipsetType)(&aChipset);
6561
6562 if (aChipset != ChipsetType_ICH9)
6563 {
6564 return setError(E_INVALIDARG,
6565 tr("Host PCI attachment only supported with ICH9 chipset"));
6566 }
6567
6568 // check if device with this host PCI address already attached
6569 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6570 it != mHWData->mPCIDeviceAssignments.end();
6571 ++it)
6572 {
6573 LONG iHostAddress = -1;
6574 ComPtr<PCIDeviceAttachment> pAttach;
6575 pAttach = *it;
6576 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6577 if (iHostAddress == hostAddress)
6578 return setError(E_INVALIDARG,
6579 tr("Device with host PCI address already attached to this VM"));
6580 }
6581
6582 ComObjPtr<PCIDeviceAttachment> pda;
6583 char name[32];
6584
6585 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6586 Bstr bname(name);
6587 pda.createObject();
6588 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6589 setModified(IsModified_MachineData);
6590 mHWData.backup();
6591 mHWData->mPCIDeviceAssignments.push_back(pda);
6592 }
6593
6594 return S_OK;
6595}
6596
6597/**
6598 * Currently this method doesn't detach device from the running VM,
6599 * just makes sure it's not plugged on next VM start.
6600 */
6601STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6602{
6603 AutoCaller autoCaller(this);
6604 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6605
6606 ComObjPtr<PCIDeviceAttachment> pAttach;
6607 bool fRemoved = false;
6608 HRESULT rc;
6609
6610 // lock scope
6611 {
6612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6613
6614 rc = checkStateDependency(MutableStateDep);
6615 if (FAILED(rc)) return rc;
6616
6617 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6618 it != mHWData->mPCIDeviceAssignments.end();
6619 ++it)
6620 {
6621 LONG iHostAddress = -1;
6622 pAttach = *it;
6623 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6624 if (iHostAddress != -1 && iHostAddress == hostAddress)
6625 {
6626 setModified(IsModified_MachineData);
6627 mHWData.backup();
6628 mHWData->mPCIDeviceAssignments.remove(pAttach);
6629 fRemoved = true;
6630 break;
6631 }
6632 }
6633 }
6634
6635
6636 /* Fire event outside of the lock */
6637 if (fRemoved)
6638 {
6639 Assert(!pAttach.isNull());
6640 ComPtr<IEventSource> es;
6641 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6642 Assert(SUCCEEDED(rc));
6643 Bstr mid;
6644 rc = this->COMGETTER(Id)(mid.asOutParam());
6645 Assert(SUCCEEDED(rc));
6646 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6647 }
6648
6649 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6650 tr("No host PCI device %08x attached"),
6651 hostAddress
6652 );
6653}
6654
6655STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6656{
6657 CheckComArgOutSafeArrayPointerValid(aAssignments);
6658
6659 AutoCaller autoCaller(this);
6660 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6661
6662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6663
6664 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6665 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6666
6667 return S_OK;
6668}
6669
6670STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6671{
6672 CheckComArgOutPointerValid(aBandwidthControl);
6673
6674 AutoCaller autoCaller(this);
6675 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6676
6677 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6678
6679 return S_OK;
6680}
6681
6682STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6683{
6684 CheckComArgOutPointerValid(pfEnabled);
6685 AutoCaller autoCaller(this);
6686 HRESULT hrc = autoCaller.rc();
6687 if (SUCCEEDED(hrc))
6688 {
6689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6690 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6691 }
6692 return hrc;
6693}
6694
6695STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6696{
6697 AutoCaller autoCaller(this);
6698 HRESULT hrc = autoCaller.rc();
6699 if (SUCCEEDED(hrc))
6700 {
6701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6702 hrc = checkStateDependency(MutableStateDep);
6703 if (SUCCEEDED(hrc))
6704 {
6705 hrc = mHWData.backupEx();
6706 if (SUCCEEDED(hrc))
6707 {
6708 setModified(IsModified_MachineData);
6709 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6710 }
6711 }
6712 }
6713 return hrc;
6714}
6715
6716STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6717{
6718 CheckComArgOutPointerValid(pbstrConfig);
6719 AutoCaller autoCaller(this);
6720 HRESULT hrc = autoCaller.rc();
6721 if (SUCCEEDED(hrc))
6722 {
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6725 }
6726 return hrc;
6727}
6728
6729STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6730{
6731 CheckComArgStr(bstrConfig);
6732 AutoCaller autoCaller(this);
6733 HRESULT hrc = autoCaller.rc();
6734 if (SUCCEEDED(hrc))
6735 {
6736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6737 hrc = checkStateDependency(MutableStateDep);
6738 if (SUCCEEDED(hrc))
6739 {
6740 hrc = mHWData.backupEx();
6741 if (SUCCEEDED(hrc))
6742 {
6743 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6744 if (SUCCEEDED(hrc))
6745 setModified(IsModified_MachineData);
6746 }
6747 }
6748 }
6749 return hrc;
6750
6751}
6752
6753STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6754{
6755 CheckComArgOutPointerValid(pfAllow);
6756 AutoCaller autoCaller(this);
6757 HRESULT hrc = autoCaller.rc();
6758 if (SUCCEEDED(hrc))
6759 {
6760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6761 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6762 }
6763 return hrc;
6764}
6765
6766STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6767{
6768 AutoCaller autoCaller(this);
6769 HRESULT hrc = autoCaller.rc();
6770 if (SUCCEEDED(hrc))
6771 {
6772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6773 hrc = checkStateDependency(MutableStateDep);
6774 if (SUCCEEDED(hrc))
6775 {
6776 hrc = mHWData.backupEx();
6777 if (SUCCEEDED(hrc))
6778 {
6779 setModified(IsModified_MachineData);
6780 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6781 }
6782 }
6783 }
6784 return hrc;
6785}
6786
6787STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6788{
6789 CheckComArgOutPointerValid(pfEnabled);
6790 AutoCaller autoCaller(this);
6791 HRESULT hrc = autoCaller.rc();
6792 if (SUCCEEDED(hrc))
6793 {
6794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6795 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6796 }
6797 return hrc;
6798}
6799
6800STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6801{
6802 AutoCaller autoCaller(this);
6803 HRESULT hrc = autoCaller.rc();
6804 if (SUCCEEDED(hrc))
6805 {
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 hrc = checkStateDependency(MutableStateDep);
6808 if ( SUCCEEDED(hrc)
6809 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6810 {
6811 AutostartDb *autostartDb = mParent->getAutostartDb();
6812 int vrc;
6813
6814 if (fEnabled)
6815 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6816 else
6817 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6818
6819 if (RT_SUCCESS(vrc))
6820 {
6821 hrc = mHWData.backupEx();
6822 if (SUCCEEDED(hrc))
6823 {
6824 setModified(IsModified_MachineData);
6825 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6826 }
6827 }
6828 else if (vrc == VERR_NOT_SUPPORTED)
6829 hrc = setError(VBOX_E_NOT_SUPPORTED,
6830 tr("The VM autostart feature is not supported on this platform"));
6831 else if (vrc == VERR_PATH_NOT_FOUND)
6832 hrc = setError(E_FAIL,
6833 tr("The path to the autostart database is not set"));
6834 else
6835 hrc = setError(E_UNEXPECTED,
6836 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6837 fEnabled ? "Adding" : "Removing",
6838 mUserData->s.strName.c_str(), vrc);
6839 }
6840 }
6841 return hrc;
6842}
6843
6844STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6845{
6846 CheckComArgOutPointerValid(puDelay);
6847 AutoCaller autoCaller(this);
6848 HRESULT hrc = autoCaller.rc();
6849 if (SUCCEEDED(hrc))
6850 {
6851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6852 *puDelay = mHWData->mAutostart.uAutostartDelay;
6853 }
6854 return hrc;
6855}
6856
6857STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6858{
6859 AutoCaller autoCaller(this);
6860 HRESULT hrc = autoCaller.rc();
6861 if (SUCCEEDED(hrc))
6862 {
6863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 hrc = checkStateDependency(MutableStateDep);
6865 if (SUCCEEDED(hrc))
6866 {
6867 hrc = mHWData.backupEx();
6868 if (SUCCEEDED(hrc))
6869 {
6870 setModified(IsModified_MachineData);
6871 mHWData->mAutostart.uAutostartDelay = uDelay;
6872 }
6873 }
6874 }
6875 return hrc;
6876}
6877
6878STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6879{
6880 CheckComArgOutPointerValid(penmAutostopType);
6881 AutoCaller autoCaller(this);
6882 HRESULT hrc = autoCaller.rc();
6883 if (SUCCEEDED(hrc))
6884 {
6885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6886 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6887 }
6888 return hrc;
6889}
6890
6891STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6892{
6893 AutoCaller autoCaller(this);
6894 HRESULT hrc = autoCaller.rc();
6895 if (SUCCEEDED(hrc))
6896 {
6897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6898 hrc = checkStateDependency(MutableStateDep);
6899 if ( SUCCEEDED(hrc)
6900 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6901 {
6902 AutostartDb *autostartDb = mParent->getAutostartDb();
6903 int vrc;
6904
6905 if (enmAutostopType != AutostopType_Disabled)
6906 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6907 else
6908 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6909
6910 if (RT_SUCCESS(vrc))
6911 {
6912 hrc = mHWData.backupEx();
6913 if (SUCCEEDED(hrc))
6914 {
6915 setModified(IsModified_MachineData);
6916 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6917 }
6918 }
6919 else if (vrc == VERR_NOT_SUPPORTED)
6920 hrc = setError(VBOX_E_NOT_SUPPORTED,
6921 tr("The VM autostop feature is not supported on this platform"));
6922 else if (vrc == VERR_PATH_NOT_FOUND)
6923 hrc = setError(E_FAIL,
6924 tr("The path to the autostart database is not set"));
6925 else
6926 hrc = setError(E_UNEXPECTED,
6927 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6928 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6929 mUserData->s.strName.c_str(), vrc);
6930 }
6931 }
6932 return hrc;
6933}
6934
6935
6936STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6937{
6938 LogFlowFuncEnter();
6939
6940 CheckComArgNotNull(pTarget);
6941 CheckComArgOutPointerValid(pProgress);
6942
6943 /* Convert the options. */
6944 RTCList<CloneOptions_T> optList;
6945 if (options != NULL)
6946 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6947
6948 if (optList.contains(CloneOptions_Link))
6949 {
6950 if (!isSnapshotMachine())
6951 return setError(E_INVALIDARG,
6952 tr("Linked clone can only be created from a snapshot"));
6953 if (mode != CloneMode_MachineState)
6954 return setError(E_INVALIDARG,
6955 tr("Linked clone can only be created for a single machine state"));
6956 }
6957 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6958
6959 AutoCaller autoCaller(this);
6960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6961
6962
6963 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6964
6965 HRESULT rc = pWorker->start(pProgress);
6966
6967 LogFlowFuncLeave();
6968
6969 return rc;
6970}
6971
6972// public methods for internal purposes
6973/////////////////////////////////////////////////////////////////////////////
6974
6975/**
6976 * Adds the given IsModified_* flag to the dirty flags of the machine.
6977 * This must be called either during loadSettings or under the machine write lock.
6978 * @param fl
6979 */
6980void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6981{
6982 mData->flModifications |= fl;
6983 if (fAllowStateModification && isStateModificationAllowed())
6984 mData->mCurrentStateModified = true;
6985}
6986
6987/**
6988 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6989 * care of the write locking.
6990 *
6991 * @param fModifications The flag to add.
6992 */
6993void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6994{
6995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6996 setModified(fModification, fAllowStateModification);
6997}
6998
6999/**
7000 * Saves the registry entry of this machine to the given configuration node.
7001 *
7002 * @param aEntryNode Node to save the registry entry to.
7003 *
7004 * @note locks this object for reading.
7005 */
7006HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7007{
7008 AutoLimitedCaller autoCaller(this);
7009 AssertComRCReturnRC(autoCaller.rc());
7010
7011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7012
7013 data.uuid = mData->mUuid;
7014 data.strSettingsFile = mData->m_strConfigFile;
7015
7016 return S_OK;
7017}
7018
7019/**
7020 * Calculates the absolute path of the given path taking the directory of the
7021 * machine settings file as the current directory.
7022 *
7023 * @param aPath Path to calculate the absolute path for.
7024 * @param aResult Where to put the result (used only on success, can be the
7025 * same Utf8Str instance as passed in @a aPath).
7026 * @return IPRT result.
7027 *
7028 * @note Locks this object for reading.
7029 */
7030int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7031{
7032 AutoCaller autoCaller(this);
7033 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7034
7035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7036
7037 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7038
7039 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7040
7041 strSettingsDir.stripFilename();
7042 char folder[RTPATH_MAX];
7043 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7044 if (RT_SUCCESS(vrc))
7045 aResult = folder;
7046
7047 return vrc;
7048}
7049
7050/**
7051 * Copies strSource to strTarget, making it relative to the machine folder
7052 * if it is a subdirectory thereof, or simply copying it otherwise.
7053 *
7054 * @param strSource Path to evaluate and copy.
7055 * @param strTarget Buffer to receive target path.
7056 *
7057 * @note Locks this object for reading.
7058 */
7059void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7060 Utf8Str &strTarget)
7061{
7062 AutoCaller autoCaller(this);
7063 AssertComRCReturn(autoCaller.rc(), (void)0);
7064
7065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7066
7067 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7068 // use strTarget as a temporary buffer to hold the machine settings dir
7069 strTarget = mData->m_strConfigFileFull;
7070 strTarget.stripFilename();
7071 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7072 {
7073 // is relative: then append what's left
7074 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7075 // for empty paths (only possible for subdirs) use "." to avoid
7076 // triggering default settings for not present config attributes.
7077 if (strTarget.isEmpty())
7078 strTarget = ".";
7079 }
7080 else
7081 // is not relative: then overwrite
7082 strTarget = strSource;
7083}
7084
7085/**
7086 * Returns the full path to the machine's log folder in the
7087 * \a aLogFolder argument.
7088 */
7089void Machine::getLogFolder(Utf8Str &aLogFolder)
7090{
7091 AutoCaller autoCaller(this);
7092 AssertComRCReturnVoid(autoCaller.rc());
7093
7094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7095
7096 char szTmp[RTPATH_MAX];
7097 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7098 if (RT_SUCCESS(vrc))
7099 {
7100 if (szTmp[0] && !mUserData.isNull())
7101 {
7102 char szTmp2[RTPATH_MAX];
7103 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7104 if (RT_SUCCESS(vrc))
7105 aLogFolder = BstrFmt("%s%c%s",
7106 szTmp2,
7107 RTPATH_DELIMITER,
7108 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7109 }
7110 else
7111 vrc = VERR_PATH_IS_RELATIVE;
7112 }
7113
7114 if (RT_FAILURE(vrc))
7115 {
7116 // fallback if VBOX_USER_LOGHOME is not set or invalid
7117 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7118 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7119 aLogFolder.append(RTPATH_DELIMITER);
7120 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7121 }
7122}
7123
7124/**
7125 * Returns the full path to the machine's log file for an given index.
7126 */
7127Utf8Str Machine::queryLogFilename(ULONG idx)
7128{
7129 Utf8Str logFolder;
7130 getLogFolder(logFolder);
7131 Assert(logFolder.length());
7132 Utf8Str log;
7133 if (idx == 0)
7134 log = Utf8StrFmt("%s%cVBox.log",
7135 logFolder.c_str(), RTPATH_DELIMITER);
7136 else
7137 log = Utf8StrFmt("%s%cVBox.log.%d",
7138 logFolder.c_str(), RTPATH_DELIMITER, idx);
7139 return log;
7140}
7141
7142/**
7143 * Composes a unique saved state filename based on the current system time. The filename is
7144 * granular to the second so this will work so long as no more than one snapshot is taken on
7145 * a machine per second.
7146 *
7147 * Before version 4.1, we used this formula for saved state files:
7148 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7149 * which no longer works because saved state files can now be shared between the saved state of the
7150 * "saved" machine and an online snapshot, and the following would cause problems:
7151 * 1) save machine
7152 * 2) create online snapshot from that machine state --> reusing saved state file
7153 * 3) save machine again --> filename would be reused, breaking the online snapshot
7154 *
7155 * So instead we now use a timestamp.
7156 *
7157 * @param str
7158 */
7159void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7160{
7161 AutoCaller autoCaller(this);
7162 AssertComRCReturnVoid(autoCaller.rc());
7163
7164 {
7165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7166 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7167 }
7168
7169 RTTIMESPEC ts;
7170 RTTimeNow(&ts);
7171 RTTIME time;
7172 RTTimeExplode(&time, &ts);
7173
7174 strStateFilePath += RTPATH_DELIMITER;
7175 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7176 time.i32Year, time.u8Month, time.u8MonthDay,
7177 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7178}
7179
7180/**
7181 * @note Locks this object for writing, calls the client process
7182 * (inside the lock).
7183 */
7184HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7185 const Utf8Str &strType,
7186 const Utf8Str &strEnvironment,
7187 ProgressProxy *aProgress)
7188{
7189 LogFlowThisFuncEnter();
7190
7191 AssertReturn(aControl, E_FAIL);
7192 AssertReturn(aProgress, E_FAIL);
7193
7194 AutoCaller autoCaller(this);
7195 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7196
7197 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7198
7199 if (!mData->mRegistered)
7200 return setError(E_UNEXPECTED,
7201 tr("The machine '%s' is not registered"),
7202 mUserData->s.strName.c_str());
7203
7204 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7205
7206 if ( mData->mSession.mState == SessionState_Locked
7207 || mData->mSession.mState == SessionState_Spawning
7208 || mData->mSession.mState == SessionState_Unlocking)
7209 return setError(VBOX_E_INVALID_OBJECT_STATE,
7210 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7211 mUserData->s.strName.c_str());
7212
7213 /* may not be busy */
7214 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7215
7216 /* get the path to the executable */
7217 char szPath[RTPATH_MAX];
7218 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7219 size_t sz = strlen(szPath);
7220 szPath[sz++] = RTPATH_DELIMITER;
7221 szPath[sz] = 0;
7222 char *cmd = szPath + sz;
7223 sz = RTPATH_MAX - sz;
7224
7225 int vrc = VINF_SUCCESS;
7226 RTPROCESS pid = NIL_RTPROCESS;
7227
7228 RTENV env = RTENV_DEFAULT;
7229
7230 if (!strEnvironment.isEmpty())
7231 {
7232 char *newEnvStr = NULL;
7233
7234 do
7235 {
7236 /* clone the current environment */
7237 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7238 AssertRCBreakStmt(vrc2, vrc = vrc2);
7239
7240 newEnvStr = RTStrDup(strEnvironment.c_str());
7241 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7242
7243 /* put new variables to the environment
7244 * (ignore empty variable names here since RTEnv API
7245 * intentionally doesn't do that) */
7246 char *var = newEnvStr;
7247 for (char *p = newEnvStr; *p; ++p)
7248 {
7249 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7250 {
7251 *p = '\0';
7252 if (*var)
7253 {
7254 char *val = strchr(var, '=');
7255 if (val)
7256 {
7257 *val++ = '\0';
7258 vrc2 = RTEnvSetEx(env, var, val);
7259 }
7260 else
7261 vrc2 = RTEnvUnsetEx(env, var);
7262 if (RT_FAILURE(vrc2))
7263 break;
7264 }
7265 var = p + 1;
7266 }
7267 }
7268 if (RT_SUCCESS(vrc2) && *var)
7269 vrc2 = RTEnvPutEx(env, var);
7270
7271 AssertRCBreakStmt(vrc2, vrc = vrc2);
7272 }
7273 while (0);
7274
7275 if (newEnvStr != NULL)
7276 RTStrFree(newEnvStr);
7277 }
7278
7279 /* Qt is default */
7280#ifdef VBOX_WITH_QTGUI
7281 if (strType == "gui" || strType == "GUI/Qt")
7282 {
7283# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7284 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7285# else
7286 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7287# endif
7288 Assert(sz >= sizeof(VirtualBox_exe));
7289 strcpy(cmd, VirtualBox_exe);
7290
7291 Utf8Str idStr = mData->mUuid.toString();
7292 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7293 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7294 }
7295#else /* !VBOX_WITH_QTGUI */
7296 if (0)
7297 ;
7298#endif /* VBOX_WITH_QTGUI */
7299
7300 else
7301
7302#ifdef VBOX_WITH_VBOXSDL
7303 if (strType == "sdl" || strType == "GUI/SDL")
7304 {
7305 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7306 Assert(sz >= sizeof(VBoxSDL_exe));
7307 strcpy(cmd, VBoxSDL_exe);
7308
7309 Utf8Str idStr = mData->mUuid.toString();
7310 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7311 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7312 }
7313#else /* !VBOX_WITH_VBOXSDL */
7314 if (0)
7315 ;
7316#endif /* !VBOX_WITH_VBOXSDL */
7317
7318 else
7319
7320#ifdef VBOX_WITH_HEADLESS
7321 if ( strType == "headless"
7322 || strType == "capture"
7323 || strType == "vrdp" /* Deprecated. Same as headless. */
7324 )
7325 {
7326 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7327 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7328 * and a VM works even if the server has not been installed.
7329 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7330 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7331 * differently in 4.0 and 3.x.
7332 */
7333 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7334 Assert(sz >= sizeof(VBoxHeadless_exe));
7335 strcpy(cmd, VBoxHeadless_exe);
7336
7337 Utf8Str idStr = mData->mUuid.toString();
7338 /* Leave space for "--capture" arg. */
7339 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7340 "--startvm", idStr.c_str(),
7341 "--vrde", "config",
7342 0, /* For "--capture". */
7343 0 };
7344 if (strType == "capture")
7345 {
7346 unsigned pos = RT_ELEMENTS(args) - 2;
7347 args[pos] = "--capture";
7348 }
7349 vrc = RTProcCreate(szPath, args, env,
7350#ifdef RT_OS_WINDOWS
7351 RTPROC_FLAGS_NO_WINDOW
7352#else
7353 0
7354#endif
7355 , &pid);
7356 }
7357#else /* !VBOX_WITH_HEADLESS */
7358 if (0)
7359 ;
7360#endif /* !VBOX_WITH_HEADLESS */
7361 else
7362 {
7363 RTEnvDestroy(env);
7364 return setError(E_INVALIDARG,
7365 tr("Invalid session type: '%s'"),
7366 strType.c_str());
7367 }
7368
7369 RTEnvDestroy(env);
7370
7371 if (RT_FAILURE(vrc))
7372 return setError(VBOX_E_IPRT_ERROR,
7373 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7374 mUserData->s.strName.c_str(), vrc);
7375
7376 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7377
7378 /*
7379 * Note that we don't release the lock here before calling the client,
7380 * because it doesn't need to call us back if called with a NULL argument.
7381 * Releasing the lock here is dangerous because we didn't prepare the
7382 * launch data yet, but the client we've just started may happen to be
7383 * too fast and call openSession() that will fail (because of PID, etc.),
7384 * so that the Machine will never get out of the Spawning session state.
7385 */
7386
7387 /* inform the session that it will be a remote one */
7388 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7389 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7390 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7391
7392 if (FAILED(rc))
7393 {
7394 /* restore the session state */
7395 mData->mSession.mState = SessionState_Unlocked;
7396 /* The failure may occur w/o any error info (from RPC), so provide one */
7397 return setError(VBOX_E_VM_ERROR,
7398 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7399 }
7400
7401 /* attach launch data to the machine */
7402 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7403 mData->mSession.mRemoteControls.push_back(aControl);
7404 mData->mSession.mProgress = aProgress;
7405 mData->mSession.mPID = pid;
7406 mData->mSession.mState = SessionState_Spawning;
7407 mData->mSession.mType = strType;
7408
7409 LogFlowThisFuncLeave();
7410 return S_OK;
7411}
7412
7413/**
7414 * Returns @c true if the given machine has an open direct session and returns
7415 * the session machine instance and additional session data (on some platforms)
7416 * if so.
7417 *
7418 * Note that when the method returns @c false, the arguments remain unchanged.
7419 *
7420 * @param aMachine Session machine object.
7421 * @param aControl Direct session control object (optional).
7422 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7423 *
7424 * @note locks this object for reading.
7425 */
7426#if defined(RT_OS_WINDOWS)
7427bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7428 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7429 HANDLE *aIPCSem /*= NULL*/,
7430 bool aAllowClosing /*= false*/)
7431#elif defined(RT_OS_OS2)
7432bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7433 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7434 HMTX *aIPCSem /*= NULL*/,
7435 bool aAllowClosing /*= false*/)
7436#else
7437bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7438 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7439 bool aAllowClosing /*= false*/)
7440#endif
7441{
7442 AutoLimitedCaller autoCaller(this);
7443 AssertComRCReturn(autoCaller.rc(), false);
7444
7445 /* just return false for inaccessible machines */
7446 if (autoCaller.state() != Ready)
7447 return false;
7448
7449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7450
7451 if ( mData->mSession.mState == SessionState_Locked
7452 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7453 )
7454 {
7455 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7456
7457 aMachine = mData->mSession.mMachine;
7458
7459 if (aControl != NULL)
7460 *aControl = mData->mSession.mDirectControl;
7461
7462#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7463 /* Additional session data */
7464 if (aIPCSem != NULL)
7465 *aIPCSem = aMachine->mIPCSem;
7466#endif
7467 return true;
7468 }
7469
7470 return false;
7471}
7472
7473/**
7474 * Returns @c true if the given machine has an spawning direct session and
7475 * returns and additional session data (on some platforms) if so.
7476 *
7477 * Note that when the method returns @c false, the arguments remain unchanged.
7478 *
7479 * @param aPID PID of the spawned direct session process.
7480 *
7481 * @note locks this object for reading.
7482 */
7483#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7484bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7485#else
7486bool Machine::isSessionSpawning()
7487#endif
7488{
7489 AutoLimitedCaller autoCaller(this);
7490 AssertComRCReturn(autoCaller.rc(), false);
7491
7492 /* just return false for inaccessible machines */
7493 if (autoCaller.state() != Ready)
7494 return false;
7495
7496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7497
7498 if (mData->mSession.mState == SessionState_Spawning)
7499 {
7500#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7501 /* Additional session data */
7502 if (aPID != NULL)
7503 {
7504 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7505 *aPID = mData->mSession.mPID;
7506 }
7507#endif
7508 return true;
7509 }
7510
7511 return false;
7512}
7513
7514/**
7515 * Called from the client watcher thread to check for unexpected client process
7516 * death during Session_Spawning state (e.g. before it successfully opened a
7517 * direct session).
7518 *
7519 * On Win32 and on OS/2, this method is called only when we've got the
7520 * direct client's process termination notification, so it always returns @c
7521 * true.
7522 *
7523 * On other platforms, this method returns @c true if the client process is
7524 * terminated and @c false if it's still alive.
7525 *
7526 * @note Locks this object for writing.
7527 */
7528bool Machine::checkForSpawnFailure()
7529{
7530 AutoCaller autoCaller(this);
7531 if (!autoCaller.isOk())
7532 {
7533 /* nothing to do */
7534 LogFlowThisFunc(("Already uninitialized!\n"));
7535 return true;
7536 }
7537
7538 /* VirtualBox::addProcessToReap() needs a write lock */
7539 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7540
7541 if (mData->mSession.mState != SessionState_Spawning)
7542 {
7543 /* nothing to do */
7544 LogFlowThisFunc(("Not spawning any more!\n"));
7545 return true;
7546 }
7547
7548 HRESULT rc = S_OK;
7549
7550#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7551
7552 /* the process was already unexpectedly terminated, we just need to set an
7553 * error and finalize session spawning */
7554 rc = setError(E_FAIL,
7555 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7556 getName().c_str());
7557#else
7558
7559 /* PID not yet initialized, skip check. */
7560 if (mData->mSession.mPID == NIL_RTPROCESS)
7561 return false;
7562
7563 RTPROCSTATUS status;
7564 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7565 &status);
7566
7567 if (vrc != VERR_PROCESS_RUNNING)
7568 {
7569 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7570 rc = setError(E_FAIL,
7571 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7572 getName().c_str(), status.iStatus);
7573 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7574 rc = setError(E_FAIL,
7575 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7576 getName().c_str(), status.iStatus);
7577 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7578 rc = setError(E_FAIL,
7579 tr("The virtual machine '%s' has terminated abnormally"),
7580 getName().c_str(), status.iStatus);
7581 else
7582 rc = setError(E_FAIL,
7583 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7584 getName().c_str(), rc);
7585 }
7586
7587#endif
7588
7589 if (FAILED(rc))
7590 {
7591 /* Close the remote session, remove the remote control from the list
7592 * and reset session state to Closed (@note keep the code in sync with
7593 * the relevant part in checkForSpawnFailure()). */
7594
7595 Assert(mData->mSession.mRemoteControls.size() == 1);
7596 if (mData->mSession.mRemoteControls.size() == 1)
7597 {
7598 ErrorInfoKeeper eik;
7599 mData->mSession.mRemoteControls.front()->Uninitialize();
7600 }
7601
7602 mData->mSession.mRemoteControls.clear();
7603 mData->mSession.mState = SessionState_Unlocked;
7604
7605 /* finalize the progress after setting the state */
7606 if (!mData->mSession.mProgress.isNull())
7607 {
7608 mData->mSession.mProgress->notifyComplete(rc);
7609 mData->mSession.mProgress.setNull();
7610 }
7611
7612 mParent->addProcessToReap(mData->mSession.mPID);
7613 mData->mSession.mPID = NIL_RTPROCESS;
7614
7615 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7616 return true;
7617 }
7618
7619 return false;
7620}
7621
7622/**
7623 * Checks whether the machine can be registered. If so, commits and saves
7624 * all settings.
7625 *
7626 * @note Must be called from mParent's write lock. Locks this object and
7627 * children for writing.
7628 */
7629HRESULT Machine::prepareRegister()
7630{
7631 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7632
7633 AutoLimitedCaller autoCaller(this);
7634 AssertComRCReturnRC(autoCaller.rc());
7635
7636 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7637
7638 /* wait for state dependents to drop to zero */
7639 ensureNoStateDependencies();
7640
7641 if (!mData->mAccessible)
7642 return setError(VBOX_E_INVALID_OBJECT_STATE,
7643 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7644 mUserData->s.strName.c_str(),
7645 mData->mUuid.toString().c_str());
7646
7647 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7648
7649 if (mData->mRegistered)
7650 return setError(VBOX_E_INVALID_OBJECT_STATE,
7651 tr("The machine '%s' with UUID {%s} is already registered"),
7652 mUserData->s.strName.c_str(),
7653 mData->mUuid.toString().c_str());
7654
7655 HRESULT rc = S_OK;
7656
7657 // Ensure the settings are saved. If we are going to be registered and
7658 // no config file exists yet, create it by calling saveSettings() too.
7659 if ( (mData->flModifications)
7660 || (!mData->pMachineConfigFile->fileExists())
7661 )
7662 {
7663 rc = saveSettings(NULL);
7664 // no need to check whether VirtualBox.xml needs saving too since
7665 // we can't have a machine XML file rename pending
7666 if (FAILED(rc)) return rc;
7667 }
7668
7669 /* more config checking goes here */
7670
7671 if (SUCCEEDED(rc))
7672 {
7673 /* we may have had implicit modifications we want to fix on success */
7674 commit();
7675
7676 mData->mRegistered = true;
7677 }
7678 else
7679 {
7680 /* we may have had implicit modifications we want to cancel on failure*/
7681 rollback(false /* aNotify */);
7682 }
7683
7684 return rc;
7685}
7686
7687/**
7688 * Increases the number of objects dependent on the machine state or on the
7689 * registered state. Guarantees that these two states will not change at least
7690 * until #releaseStateDependency() is called.
7691 *
7692 * Depending on the @a aDepType value, additional state checks may be made.
7693 * These checks will set extended error info on failure. See
7694 * #checkStateDependency() for more info.
7695 *
7696 * If this method returns a failure, the dependency is not added and the caller
7697 * is not allowed to rely on any particular machine state or registration state
7698 * value and may return the failed result code to the upper level.
7699 *
7700 * @param aDepType Dependency type to add.
7701 * @param aState Current machine state (NULL if not interested).
7702 * @param aRegistered Current registered state (NULL if not interested).
7703 *
7704 * @note Locks this object for writing.
7705 */
7706HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7707 MachineState_T *aState /* = NULL */,
7708 BOOL *aRegistered /* = NULL */)
7709{
7710 AutoCaller autoCaller(this);
7711 AssertComRCReturnRC(autoCaller.rc());
7712
7713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7714
7715 HRESULT rc = checkStateDependency(aDepType);
7716 if (FAILED(rc)) return rc;
7717
7718 {
7719 if (mData->mMachineStateChangePending != 0)
7720 {
7721 /* ensureNoStateDependencies() is waiting for state dependencies to
7722 * drop to zero so don't add more. It may make sense to wait a bit
7723 * and retry before reporting an error (since the pending state
7724 * transition should be really quick) but let's just assert for
7725 * now to see if it ever happens on practice. */
7726
7727 AssertFailed();
7728
7729 return setError(E_ACCESSDENIED,
7730 tr("Machine state change is in progress. Please retry the operation later."));
7731 }
7732
7733 ++mData->mMachineStateDeps;
7734 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7735 }
7736
7737 if (aState)
7738 *aState = mData->mMachineState;
7739 if (aRegistered)
7740 *aRegistered = mData->mRegistered;
7741
7742 return S_OK;
7743}
7744
7745/**
7746 * Decreases the number of objects dependent on the machine state.
7747 * Must always complete the #addStateDependency() call after the state
7748 * dependency is no more necessary.
7749 */
7750void Machine::releaseStateDependency()
7751{
7752 AutoCaller autoCaller(this);
7753 AssertComRCReturnVoid(autoCaller.rc());
7754
7755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7756
7757 /* releaseStateDependency() w/o addStateDependency()? */
7758 AssertReturnVoid(mData->mMachineStateDeps != 0);
7759 -- mData->mMachineStateDeps;
7760
7761 if (mData->mMachineStateDeps == 0)
7762 {
7763 /* inform ensureNoStateDependencies() that there are no more deps */
7764 if (mData->mMachineStateChangePending != 0)
7765 {
7766 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7767 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7768 }
7769 }
7770}
7771
7772// protected methods
7773/////////////////////////////////////////////////////////////////////////////
7774
7775/**
7776 * Performs machine state checks based on the @a aDepType value. If a check
7777 * fails, this method will set extended error info, otherwise it will return
7778 * S_OK. It is supposed, that on failure, the caller will immediately return
7779 * the return value of this method to the upper level.
7780 *
7781 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7782 *
7783 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7784 * current state of this machine object allows to change settings of the
7785 * machine (i.e. the machine is not registered, or registered but not running
7786 * and not saved). It is useful to call this method from Machine setters
7787 * before performing any change.
7788 *
7789 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7790 * as for MutableStateDep except that if the machine is saved, S_OK is also
7791 * returned. This is useful in setters which allow changing machine
7792 * properties when it is in the saved state.
7793 *
7794 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7795 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7796 * Aborted).
7797 *
7798 * @param aDepType Dependency type to check.
7799 *
7800 * @note Non Machine based classes should use #addStateDependency() and
7801 * #releaseStateDependency() methods or the smart AutoStateDependency
7802 * template.
7803 *
7804 * @note This method must be called from under this object's read or write
7805 * lock.
7806 */
7807HRESULT Machine::checkStateDependency(StateDependency aDepType)
7808{
7809 switch (aDepType)
7810 {
7811 case AnyStateDep:
7812 {
7813 break;
7814 }
7815 case MutableStateDep:
7816 {
7817 if ( mData->mRegistered
7818 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7819 || ( mData->mMachineState != MachineState_Paused
7820 && mData->mMachineState != MachineState_Running
7821 && mData->mMachineState != MachineState_Aborted
7822 && mData->mMachineState != MachineState_Teleported
7823 && mData->mMachineState != MachineState_PoweredOff
7824 )
7825 )
7826 )
7827 return setError(VBOX_E_INVALID_VM_STATE,
7828 tr("The machine is not mutable (state is %s)"),
7829 Global::stringifyMachineState(mData->mMachineState));
7830 break;
7831 }
7832 case MutableOrSavedStateDep:
7833 {
7834 if ( mData->mRegistered
7835 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7836 || ( mData->mMachineState != MachineState_Paused
7837 && mData->mMachineState != MachineState_Running
7838 && mData->mMachineState != MachineState_Aborted
7839 && mData->mMachineState != MachineState_Teleported
7840 && mData->mMachineState != MachineState_Saved
7841 && mData->mMachineState != MachineState_PoweredOff
7842 )
7843 )
7844 )
7845 return setError(VBOX_E_INVALID_VM_STATE,
7846 tr("The machine is not mutable (state is %s)"),
7847 Global::stringifyMachineState(mData->mMachineState));
7848 break;
7849 }
7850 case OfflineStateDep:
7851 {
7852 if ( mData->mRegistered
7853 && ( !isSessionMachine()
7854 || ( mData->mMachineState != MachineState_PoweredOff
7855 && mData->mMachineState != MachineState_Saved
7856 && mData->mMachineState != MachineState_Aborted
7857 && mData->mMachineState != MachineState_Teleported
7858 )
7859 )
7860 )
7861 return setError(VBOX_E_INVALID_VM_STATE,
7862 tr("The machine is not offline (state is %s)"),
7863 Global::stringifyMachineState(mData->mMachineState));
7864 break;
7865 }
7866 }
7867
7868 return S_OK;
7869}
7870
7871/**
7872 * Helper to initialize all associated child objects and allocate data
7873 * structures.
7874 *
7875 * This method must be called as a part of the object's initialization procedure
7876 * (usually done in the #init() method).
7877 *
7878 * @note Must be called only from #init() or from #registeredInit().
7879 */
7880HRESULT Machine::initDataAndChildObjects()
7881{
7882 AutoCaller autoCaller(this);
7883 AssertComRCReturnRC(autoCaller.rc());
7884 AssertComRCReturn(autoCaller.state() == InInit ||
7885 autoCaller.state() == Limited, E_FAIL);
7886
7887 AssertReturn(!mData->mAccessible, E_FAIL);
7888
7889 /* allocate data structures */
7890 mSSData.allocate();
7891 mUserData.allocate();
7892 mHWData.allocate();
7893 mMediaData.allocate();
7894 mStorageControllers.allocate();
7895
7896 /* initialize mOSTypeId */
7897 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7898
7899 /* create associated BIOS settings object */
7900 unconst(mBIOSSettings).createObject();
7901 mBIOSSettings->init(this);
7902
7903 /* create an associated VRDE object (default is disabled) */
7904 unconst(mVRDEServer).createObject();
7905 mVRDEServer->init(this);
7906
7907 /* create associated serial port objects */
7908 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7909 {
7910 unconst(mSerialPorts[slot]).createObject();
7911 mSerialPorts[slot]->init(this, slot);
7912 }
7913
7914 /* create associated parallel port objects */
7915 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7916 {
7917 unconst(mParallelPorts[slot]).createObject();
7918 mParallelPorts[slot]->init(this, slot);
7919 }
7920
7921 /* create the audio adapter object (always present, default is disabled) */
7922 unconst(mAudioAdapter).createObject();
7923 mAudioAdapter->init(this);
7924
7925 /* create the USB controller object (always present, default is disabled) */
7926 unconst(mUSBController).createObject();
7927 mUSBController->init(this);
7928
7929 /* create associated network adapter objects */
7930 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7931 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7932 {
7933 unconst(mNetworkAdapters[slot]).createObject();
7934 mNetworkAdapters[slot]->init(this, slot);
7935 }
7936
7937 /* create the bandwidth control */
7938 unconst(mBandwidthControl).createObject();
7939 mBandwidthControl->init(this);
7940
7941 return S_OK;
7942}
7943
7944/**
7945 * Helper to uninitialize all associated child objects and to free all data
7946 * structures.
7947 *
7948 * This method must be called as a part of the object's uninitialization
7949 * procedure (usually done in the #uninit() method).
7950 *
7951 * @note Must be called only from #uninit() or from #registeredInit().
7952 */
7953void Machine::uninitDataAndChildObjects()
7954{
7955 AutoCaller autoCaller(this);
7956 AssertComRCReturnVoid(autoCaller.rc());
7957 AssertComRCReturnVoid( autoCaller.state() == InUninit
7958 || autoCaller.state() == Limited);
7959
7960 /* tell all our other child objects we've been uninitialized */
7961 if (mBandwidthControl)
7962 {
7963 mBandwidthControl->uninit();
7964 unconst(mBandwidthControl).setNull();
7965 }
7966
7967 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7968 {
7969 if (mNetworkAdapters[slot])
7970 {
7971 mNetworkAdapters[slot]->uninit();
7972 unconst(mNetworkAdapters[slot]).setNull();
7973 }
7974 }
7975
7976 if (mUSBController)
7977 {
7978 mUSBController->uninit();
7979 unconst(mUSBController).setNull();
7980 }
7981
7982 if (mAudioAdapter)
7983 {
7984 mAudioAdapter->uninit();
7985 unconst(mAudioAdapter).setNull();
7986 }
7987
7988 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7989 {
7990 if (mParallelPorts[slot])
7991 {
7992 mParallelPorts[slot]->uninit();
7993 unconst(mParallelPorts[slot]).setNull();
7994 }
7995 }
7996
7997 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7998 {
7999 if (mSerialPorts[slot])
8000 {
8001 mSerialPorts[slot]->uninit();
8002 unconst(mSerialPorts[slot]).setNull();
8003 }
8004 }
8005
8006 if (mVRDEServer)
8007 {
8008 mVRDEServer->uninit();
8009 unconst(mVRDEServer).setNull();
8010 }
8011
8012 if (mBIOSSettings)
8013 {
8014 mBIOSSettings->uninit();
8015 unconst(mBIOSSettings).setNull();
8016 }
8017
8018 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
8019 * instance is uninitialized; SessionMachine instances refer to real
8020 * Machine hard disks). This is necessary for a clean re-initialization of
8021 * the VM after successfully re-checking the accessibility state. Note
8022 * that in case of normal Machine or SnapshotMachine uninitialization (as
8023 * a result of unregistering or deleting the snapshot), outdated hard
8024 * disk attachments will already be uninitialized and deleted, so this
8025 * code will not affect them. */
8026 if ( !!mMediaData
8027 && (!isSessionMachine())
8028 )
8029 {
8030 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8031 it != mMediaData->mAttachments.end();
8032 ++it)
8033 {
8034 ComObjPtr<Medium> hd = (*it)->getMedium();
8035 if (hd.isNull())
8036 continue;
8037 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
8038 AssertComRC(rc);
8039 }
8040 }
8041
8042 if (!isSessionMachine() && !isSnapshotMachine())
8043 {
8044 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8045 if (mData->mFirstSnapshot)
8046 {
8047 // snapshots tree is protected by machine write lock; strictly
8048 // this isn't necessary here since we're deleting the entire
8049 // machine, but otherwise we assert in Snapshot::uninit()
8050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8051 mData->mFirstSnapshot->uninit();
8052 mData->mFirstSnapshot.setNull();
8053 }
8054
8055 mData->mCurrentSnapshot.setNull();
8056 }
8057
8058 /* free data structures (the essential mData structure is not freed here
8059 * since it may be still in use) */
8060 mMediaData.free();
8061 mStorageControllers.free();
8062 mHWData.free();
8063 mUserData.free();
8064 mSSData.free();
8065}
8066
8067/**
8068 * Returns a pointer to the Machine object for this machine that acts like a
8069 * parent for complex machine data objects such as shared folders, etc.
8070 *
8071 * For primary Machine objects and for SnapshotMachine objects, returns this
8072 * object's pointer itself. For SessionMachine objects, returns the peer
8073 * (primary) machine pointer.
8074 */
8075Machine* Machine::getMachine()
8076{
8077 if (isSessionMachine())
8078 return (Machine*)mPeer;
8079 return this;
8080}
8081
8082/**
8083 * Makes sure that there are no machine state dependents. If necessary, waits
8084 * for the number of dependents to drop to zero.
8085 *
8086 * Make sure this method is called from under this object's write lock to
8087 * guarantee that no new dependents may be added when this method returns
8088 * control to the caller.
8089 *
8090 * @note Locks this object for writing. The lock will be released while waiting
8091 * (if necessary).
8092 *
8093 * @warning To be used only in methods that change the machine state!
8094 */
8095void Machine::ensureNoStateDependencies()
8096{
8097 AssertReturnVoid(isWriteLockOnCurrentThread());
8098
8099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8100
8101 /* Wait for all state dependents if necessary */
8102 if (mData->mMachineStateDeps != 0)
8103 {
8104 /* lazy semaphore creation */
8105 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8106 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8107
8108 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8109 mData->mMachineStateDeps));
8110
8111 ++mData->mMachineStateChangePending;
8112
8113 /* reset the semaphore before waiting, the last dependent will signal
8114 * it */
8115 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8116
8117 alock.release();
8118
8119 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8120
8121 alock.acquire();
8122
8123 -- mData->mMachineStateChangePending;
8124 }
8125}
8126
8127/**
8128 * Changes the machine state and informs callbacks.
8129 *
8130 * This method is not intended to fail so it either returns S_OK or asserts (and
8131 * returns a failure).
8132 *
8133 * @note Locks this object for writing.
8134 */
8135HRESULT Machine::setMachineState(MachineState_T aMachineState)
8136{
8137 LogFlowThisFuncEnter();
8138 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8139
8140 AutoCaller autoCaller(this);
8141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8142
8143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8144
8145 /* wait for state dependents to drop to zero */
8146 ensureNoStateDependencies();
8147
8148 if (mData->mMachineState != aMachineState)
8149 {
8150 mData->mMachineState = aMachineState;
8151
8152 RTTimeNow(&mData->mLastStateChange);
8153
8154 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8155 }
8156
8157 LogFlowThisFuncLeave();
8158 return S_OK;
8159}
8160
8161/**
8162 * Searches for a shared folder with the given logical name
8163 * in the collection of shared folders.
8164 *
8165 * @param aName logical name of the shared folder
8166 * @param aSharedFolder where to return the found object
8167 * @param aSetError whether to set the error info if the folder is
8168 * not found
8169 * @return
8170 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8171 *
8172 * @note
8173 * must be called from under the object's lock!
8174 */
8175HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8176 ComObjPtr<SharedFolder> &aSharedFolder,
8177 bool aSetError /* = false */)
8178{
8179 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8180 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8181 it != mHWData->mSharedFolders.end();
8182 ++it)
8183 {
8184 SharedFolder *pSF = *it;
8185 AutoCaller autoCaller(pSF);
8186 if (pSF->getName() == aName)
8187 {
8188 aSharedFolder = pSF;
8189 rc = S_OK;
8190 break;
8191 }
8192 }
8193
8194 if (aSetError && FAILED(rc))
8195 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8196
8197 return rc;
8198}
8199
8200/**
8201 * Initializes all machine instance data from the given settings structures
8202 * from XML. The exception is the machine UUID which needs special handling
8203 * depending on the caller's use case, so the caller needs to set that herself.
8204 *
8205 * This gets called in several contexts during machine initialization:
8206 *
8207 * -- When machine XML exists on disk already and needs to be loaded into memory,
8208 * for example, from registeredInit() to load all registered machines on
8209 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8210 * attached to the machine should be part of some media registry already.
8211 *
8212 * -- During OVF import, when a machine config has been constructed from an
8213 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8214 * ensure that the media listed as attachments in the config (which have
8215 * been imported from the OVF) receive the correct registry ID.
8216 *
8217 * -- During VM cloning.
8218 *
8219 * @param config Machine settings from XML.
8220 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8221 * @return
8222 */
8223HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8224 const Guid *puuidRegistry)
8225{
8226 // copy name, description, OS type, teleporter, UTC etc.
8227 mUserData->s = config.machineUserData;
8228
8229 // look up the object by Id to check it is valid
8230 ComPtr<IGuestOSType> guestOSType;
8231 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8232 guestOSType.asOutParam());
8233 if (FAILED(rc)) return rc;
8234
8235 // stateFile (optional)
8236 if (config.strStateFile.isEmpty())
8237 mSSData->strStateFilePath.setNull();
8238 else
8239 {
8240 Utf8Str stateFilePathFull(config.strStateFile);
8241 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8242 if (RT_FAILURE(vrc))
8243 return setError(E_FAIL,
8244 tr("Invalid saved state file path '%s' (%Rrc)"),
8245 config.strStateFile.c_str(),
8246 vrc);
8247 mSSData->strStateFilePath = stateFilePathFull;
8248 }
8249
8250 // snapshot folder needs special processing so set it again
8251 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8252 if (FAILED(rc)) return rc;
8253
8254 /* Copy the extra data items (Not in any case config is already the same as
8255 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8256 * make sure the extra data map is copied). */
8257 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8258
8259 /* currentStateModified (optional, default is true) */
8260 mData->mCurrentStateModified = config.fCurrentStateModified;
8261
8262 mData->mLastStateChange = config.timeLastStateChange;
8263
8264 /*
8265 * note: all mUserData members must be assigned prior this point because
8266 * we need to commit changes in order to let mUserData be shared by all
8267 * snapshot machine instances.
8268 */
8269 mUserData.commitCopy();
8270
8271 // machine registry, if present (must be loaded before snapshots)
8272 if (config.canHaveOwnMediaRegistry())
8273 {
8274 // determine machine folder
8275 Utf8Str strMachineFolder = getSettingsFileFull();
8276 strMachineFolder.stripFilename();
8277 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8278 config.mediaRegistry,
8279 strMachineFolder);
8280 if (FAILED(rc)) return rc;
8281 }
8282
8283 /* Snapshot node (optional) */
8284 size_t cRootSnapshots;
8285 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8286 {
8287 // there must be only one root snapshot
8288 Assert(cRootSnapshots == 1);
8289
8290 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8291
8292 rc = loadSnapshot(snap,
8293 config.uuidCurrentSnapshot,
8294 NULL); // no parent == first snapshot
8295 if (FAILED(rc)) return rc;
8296 }
8297
8298 // hardware data
8299 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8300 if (FAILED(rc)) return rc;
8301
8302 // load storage controllers
8303 rc = loadStorageControllers(config.storageMachine,
8304 puuidRegistry,
8305 NULL /* puuidSnapshot */);
8306 if (FAILED(rc)) return rc;
8307
8308 /*
8309 * NOTE: the assignment below must be the last thing to do,
8310 * otherwise it will be not possible to change the settings
8311 * somewhere in the code above because all setters will be
8312 * blocked by checkStateDependency(MutableStateDep).
8313 */
8314
8315 /* set the machine state to Aborted or Saved when appropriate */
8316 if (config.fAborted)
8317 {
8318 mSSData->strStateFilePath.setNull();
8319
8320 /* no need to use setMachineState() during init() */
8321 mData->mMachineState = MachineState_Aborted;
8322 }
8323 else if (!mSSData->strStateFilePath.isEmpty())
8324 {
8325 /* no need to use setMachineState() during init() */
8326 mData->mMachineState = MachineState_Saved;
8327 }
8328
8329 // after loading settings, we are no longer different from the XML on disk
8330 mData->flModifications = 0;
8331
8332 return S_OK;
8333}
8334
8335/**
8336 * Recursively loads all snapshots starting from the given.
8337 *
8338 * @param aNode <Snapshot> node.
8339 * @param aCurSnapshotId Current snapshot ID from the settings file.
8340 * @param aParentSnapshot Parent snapshot.
8341 */
8342HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8343 const Guid &aCurSnapshotId,
8344 Snapshot *aParentSnapshot)
8345{
8346 AssertReturn(!isSnapshotMachine(), E_FAIL);
8347 AssertReturn(!isSessionMachine(), E_FAIL);
8348
8349 HRESULT rc = S_OK;
8350
8351 Utf8Str strStateFile;
8352 if (!data.strStateFile.isEmpty())
8353 {
8354 /* optional */
8355 strStateFile = data.strStateFile;
8356 int vrc = calculateFullPath(strStateFile, strStateFile);
8357 if (RT_FAILURE(vrc))
8358 return setError(E_FAIL,
8359 tr("Invalid saved state file path '%s' (%Rrc)"),
8360 strStateFile.c_str(),
8361 vrc);
8362 }
8363
8364 /* create a snapshot machine object */
8365 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8366 pSnapshotMachine.createObject();
8367 rc = pSnapshotMachine->initFromSettings(this,
8368 data.hardware,
8369 &data.debugging,
8370 &data.autostart,
8371 data.storage,
8372 data.uuid.ref(),
8373 strStateFile);
8374 if (FAILED(rc)) return rc;
8375
8376 /* create a snapshot object */
8377 ComObjPtr<Snapshot> pSnapshot;
8378 pSnapshot.createObject();
8379 /* initialize the snapshot */
8380 rc = pSnapshot->init(mParent, // VirtualBox object
8381 data.uuid,
8382 data.strName,
8383 data.strDescription,
8384 data.timestamp,
8385 pSnapshotMachine,
8386 aParentSnapshot);
8387 if (FAILED(rc)) return rc;
8388
8389 /* memorize the first snapshot if necessary */
8390 if (!mData->mFirstSnapshot)
8391 mData->mFirstSnapshot = pSnapshot;
8392
8393 /* memorize the current snapshot when appropriate */
8394 if ( !mData->mCurrentSnapshot
8395 && pSnapshot->getId() == aCurSnapshotId
8396 )
8397 mData->mCurrentSnapshot = pSnapshot;
8398
8399 // now create the children
8400 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8401 it != data.llChildSnapshots.end();
8402 ++it)
8403 {
8404 const settings::Snapshot &childData = *it;
8405 // recurse
8406 rc = loadSnapshot(childData,
8407 aCurSnapshotId,
8408 pSnapshot); // parent = the one we created above
8409 if (FAILED(rc)) return rc;
8410 }
8411
8412 return rc;
8413}
8414
8415/**
8416 * Loads settings into mHWData.
8417 *
8418 * @param data Reference to the hardware settings.
8419 * @param pDbg Pointer to the debugging settings.
8420 * @param pAutostart Pointer to the autostart settings.
8421 */
8422HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8423 const settings::Autostart *pAutostart)
8424{
8425 AssertReturn(!isSessionMachine(), E_FAIL);
8426
8427 HRESULT rc = S_OK;
8428
8429 try
8430 {
8431 /* The hardware version attribute (optional). */
8432 mHWData->mHWVersion = data.strVersion;
8433 mHWData->mHardwareUUID = data.uuid;
8434
8435 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8436 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8437 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8438 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8439 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8440 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8441 mHWData->mPAEEnabled = data.fPAE;
8442 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8443
8444 mHWData->mCPUCount = data.cCPUs;
8445 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8446 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8447
8448 // cpu
8449 if (mHWData->mCPUHotPlugEnabled)
8450 {
8451 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8452 it != data.llCpus.end();
8453 ++it)
8454 {
8455 const settings::Cpu &cpu = *it;
8456
8457 mHWData->mCPUAttached[cpu.ulId] = true;
8458 }
8459 }
8460
8461 // cpuid leafs
8462 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8463 it != data.llCpuIdLeafs.end();
8464 ++it)
8465 {
8466 const settings::CpuIdLeaf &leaf = *it;
8467
8468 switch (leaf.ulId)
8469 {
8470 case 0x0:
8471 case 0x1:
8472 case 0x2:
8473 case 0x3:
8474 case 0x4:
8475 case 0x5:
8476 case 0x6:
8477 case 0x7:
8478 case 0x8:
8479 case 0x9:
8480 case 0xA:
8481 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8482 break;
8483
8484 case 0x80000000:
8485 case 0x80000001:
8486 case 0x80000002:
8487 case 0x80000003:
8488 case 0x80000004:
8489 case 0x80000005:
8490 case 0x80000006:
8491 case 0x80000007:
8492 case 0x80000008:
8493 case 0x80000009:
8494 case 0x8000000A:
8495 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8496 break;
8497
8498 default:
8499 /* just ignore */
8500 break;
8501 }
8502 }
8503
8504 mHWData->mMemorySize = data.ulMemorySizeMB;
8505 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8506
8507 // boot order
8508 for (size_t i = 0;
8509 i < RT_ELEMENTS(mHWData->mBootOrder);
8510 i++)
8511 {
8512 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8513 if (it == data.mapBootOrder.end())
8514 mHWData->mBootOrder[i] = DeviceType_Null;
8515 else
8516 mHWData->mBootOrder[i] = it->second;
8517 }
8518
8519 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8520 mHWData->mMonitorCount = data.cMonitors;
8521 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8522 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8523 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8524 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8525 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8526 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8527 mHWData->mFirmwareType = data.firmwareType;
8528 mHWData->mPointingHIDType = data.pointingHIDType;
8529 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8530 mHWData->mChipsetType = data.chipsetType;
8531 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8532 mHWData->mHPETEnabled = data.fHPETEnabled;
8533
8534 /* VRDEServer */
8535 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8536 if (FAILED(rc)) return rc;
8537
8538 /* BIOS */
8539 rc = mBIOSSettings->loadSettings(data.biosSettings);
8540 if (FAILED(rc)) return rc;
8541
8542 // Bandwidth control (must come before network adapters)
8543 rc = mBandwidthControl->loadSettings(data.ioSettings);
8544 if (FAILED(rc)) return rc;
8545
8546 /* USB Controller */
8547 rc = mUSBController->loadSettings(data.usbController);
8548 if (FAILED(rc)) return rc;
8549
8550 // network adapters
8551 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8552 uint32_t oldCount = mNetworkAdapters.size();
8553 if (newCount > oldCount)
8554 {
8555 mNetworkAdapters.resize(newCount);
8556 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8557 {
8558 unconst(mNetworkAdapters[slot]).createObject();
8559 mNetworkAdapters[slot]->init(this, slot);
8560 }
8561 }
8562 else if (newCount < oldCount)
8563 mNetworkAdapters.resize(newCount);
8564 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8565 it != data.llNetworkAdapters.end();
8566 ++it)
8567 {
8568 const settings::NetworkAdapter &nic = *it;
8569
8570 /* slot unicity is guaranteed by XML Schema */
8571 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8572 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8573 if (FAILED(rc)) return rc;
8574 }
8575
8576 // serial ports
8577 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8578 it != data.llSerialPorts.end();
8579 ++it)
8580 {
8581 const settings::SerialPort &s = *it;
8582
8583 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8584 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8585 if (FAILED(rc)) return rc;
8586 }
8587
8588 // parallel ports (optional)
8589 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8590 it != data.llParallelPorts.end();
8591 ++it)
8592 {
8593 const settings::ParallelPort &p = *it;
8594
8595 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8596 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8597 if (FAILED(rc)) return rc;
8598 }
8599
8600 /* AudioAdapter */
8601 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8602 if (FAILED(rc)) return rc;
8603
8604 /* Shared folders */
8605 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8606 it != data.llSharedFolders.end();
8607 ++it)
8608 {
8609 const settings::SharedFolder &sf = *it;
8610
8611 ComObjPtr<SharedFolder> sharedFolder;
8612 /* Check for double entries. Not allowed! */
8613 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8614 if (SUCCEEDED(rc))
8615 return setError(VBOX_E_OBJECT_IN_USE,
8616 tr("Shared folder named '%s' already exists"),
8617 sf.strName.c_str());
8618
8619 /* Create the new shared folder. Don't break on error. This will be
8620 * reported when the machine starts. */
8621 sharedFolder.createObject();
8622 rc = sharedFolder->init(getMachine(),
8623 sf.strName,
8624 sf.strHostPath,
8625 RT_BOOL(sf.fWritable),
8626 RT_BOOL(sf.fAutoMount),
8627 false /* fFailOnError */);
8628 if (FAILED(rc)) return rc;
8629 mHWData->mSharedFolders.push_back(sharedFolder);
8630 }
8631
8632 // Clipboard
8633 mHWData->mClipboardMode = data.clipboardMode;
8634
8635 // drag'n'drop
8636 mHWData->mDragAndDropMode = data.dragAndDropMode;
8637
8638 // guest settings
8639 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8640
8641 // IO settings
8642 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8643 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8644
8645 // Host PCI devices
8646 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8647 it != data.pciAttachments.end();
8648 ++it)
8649 {
8650 const settings::HostPCIDeviceAttachment &hpda = *it;
8651 ComObjPtr<PCIDeviceAttachment> pda;
8652
8653 pda.createObject();
8654 pda->loadSettings(this, hpda);
8655 mHWData->mPCIDeviceAssignments.push_back(pda);
8656 }
8657
8658 /*
8659 * (The following isn't really real hardware, but it lives in HWData
8660 * for reasons of convenience.)
8661 */
8662
8663#ifdef VBOX_WITH_GUEST_PROPS
8664 /* Guest properties (optional) */
8665 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8666 it != data.llGuestProperties.end();
8667 ++it)
8668 {
8669 const settings::GuestProperty &prop = *it;
8670 uint32_t fFlags = guestProp::NILFLAG;
8671 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8672 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8673 mHWData->mGuestProperties.push_back(property);
8674 }
8675
8676 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8677#endif /* VBOX_WITH_GUEST_PROPS defined */
8678
8679 rc = loadDebugging(pDbg);
8680 if (FAILED(rc))
8681 return rc;
8682
8683 mHWData->mAutostart = *pAutostart;
8684 }
8685 catch(std::bad_alloc &)
8686 {
8687 return E_OUTOFMEMORY;
8688 }
8689
8690 AssertComRC(rc);
8691 return rc;
8692}
8693
8694/**
8695 * Called from Machine::loadHardware() to load the debugging settings of the
8696 * machine.
8697 *
8698 * @param pDbg Pointer to the settings.
8699 */
8700HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8701{
8702 mHWData->mDebugging = *pDbg;
8703 /* no more processing currently required, this will probably change. */
8704 return S_OK;
8705}
8706
8707/**
8708 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8709 *
8710 * @param data
8711 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8712 * @param puuidSnapshot
8713 * @return
8714 */
8715HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8716 const Guid *puuidRegistry,
8717 const Guid *puuidSnapshot)
8718{
8719 AssertReturn(!isSessionMachine(), E_FAIL);
8720
8721 HRESULT rc = S_OK;
8722
8723 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8724 it != data.llStorageControllers.end();
8725 ++it)
8726 {
8727 const settings::StorageController &ctlData = *it;
8728
8729 ComObjPtr<StorageController> pCtl;
8730 /* Try to find one with the name first. */
8731 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8732 if (SUCCEEDED(rc))
8733 return setError(VBOX_E_OBJECT_IN_USE,
8734 tr("Storage controller named '%s' already exists"),
8735 ctlData.strName.c_str());
8736
8737 pCtl.createObject();
8738 rc = pCtl->init(this,
8739 ctlData.strName,
8740 ctlData.storageBus,
8741 ctlData.ulInstance,
8742 ctlData.fBootable);
8743 if (FAILED(rc)) return rc;
8744
8745 mStorageControllers->push_back(pCtl);
8746
8747 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8748 if (FAILED(rc)) return rc;
8749
8750 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8751 if (FAILED(rc)) return rc;
8752
8753 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8754 if (FAILED(rc)) return rc;
8755
8756 /* Set IDE emulation settings (only for AHCI controller). */
8757 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8758 {
8759 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8760 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8761 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8762 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8763 )
8764 return rc;
8765 }
8766
8767 /* Load the attached devices now. */
8768 rc = loadStorageDevices(pCtl,
8769 ctlData,
8770 puuidRegistry,
8771 puuidSnapshot);
8772 if (FAILED(rc)) return rc;
8773 }
8774
8775 return S_OK;
8776}
8777
8778/**
8779 * Called from loadStorageControllers for a controller's devices.
8780 *
8781 * @param aStorageController
8782 * @param data
8783 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8784 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8785 * @return
8786 */
8787HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8788 const settings::StorageController &data,
8789 const Guid *puuidRegistry,
8790 const Guid *puuidSnapshot)
8791{
8792 HRESULT rc = S_OK;
8793
8794 /* paranoia: detect duplicate attachments */
8795 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8796 it != data.llAttachedDevices.end();
8797 ++it)
8798 {
8799 const settings::AttachedDevice &ad = *it;
8800
8801 for (settings::AttachedDevicesList::const_iterator it2 = it;
8802 it2 != data.llAttachedDevices.end();
8803 ++it2)
8804 {
8805 if (it == it2)
8806 continue;
8807
8808 const settings::AttachedDevice &ad2 = *it2;
8809
8810 if ( ad.lPort == ad2.lPort
8811 && ad.lDevice == ad2.lDevice)
8812 {
8813 return setError(E_FAIL,
8814 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8815 aStorageController->getName().c_str(),
8816 ad.lPort,
8817 ad.lDevice,
8818 mUserData->s.strName.c_str());
8819 }
8820 }
8821 }
8822
8823 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8824 it != data.llAttachedDevices.end();
8825 ++it)
8826 {
8827 const settings::AttachedDevice &dev = *it;
8828 ComObjPtr<Medium> medium;
8829
8830 switch (dev.deviceType)
8831 {
8832 case DeviceType_Floppy:
8833 case DeviceType_DVD:
8834 if (dev.strHostDriveSrc.isNotEmpty())
8835 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8836 else
8837 rc = mParent->findRemoveableMedium(dev.deviceType,
8838 dev.uuid,
8839 false /* fRefresh */,
8840 false /* aSetError */,
8841 medium);
8842 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8843 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8844 rc = S_OK;
8845 break;
8846
8847 case DeviceType_HardDisk:
8848 {
8849 /* find a hard disk by UUID */
8850 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8851 if (FAILED(rc))
8852 {
8853 if (isSnapshotMachine())
8854 {
8855 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8856 // so the user knows that the bad disk is in a snapshot somewhere
8857 com::ErrorInfo info;
8858 return setError(E_FAIL,
8859 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8860 puuidSnapshot->raw(),
8861 info.getText().raw());
8862 }
8863 else
8864 return rc;
8865 }
8866
8867 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8868
8869 if (medium->getType() == MediumType_Immutable)
8870 {
8871 if (isSnapshotMachine())
8872 return setError(E_FAIL,
8873 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8874 "of the virtual machine '%s' ('%s')"),
8875 medium->getLocationFull().c_str(),
8876 dev.uuid.raw(),
8877 puuidSnapshot->raw(),
8878 mUserData->s.strName.c_str(),
8879 mData->m_strConfigFileFull.c_str());
8880
8881 return setError(E_FAIL,
8882 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8883 medium->getLocationFull().c_str(),
8884 dev.uuid.raw(),
8885 mUserData->s.strName.c_str(),
8886 mData->m_strConfigFileFull.c_str());
8887 }
8888
8889 if (medium->getType() == MediumType_MultiAttach)
8890 {
8891 if (isSnapshotMachine())
8892 return setError(E_FAIL,
8893 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8894 "of the virtual machine '%s' ('%s')"),
8895 medium->getLocationFull().c_str(),
8896 dev.uuid.raw(),
8897 puuidSnapshot->raw(),
8898 mUserData->s.strName.c_str(),
8899 mData->m_strConfigFileFull.c_str());
8900
8901 return setError(E_FAIL,
8902 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8903 medium->getLocationFull().c_str(),
8904 dev.uuid.raw(),
8905 mUserData->s.strName.c_str(),
8906 mData->m_strConfigFileFull.c_str());
8907 }
8908
8909 if ( !isSnapshotMachine()
8910 && medium->getChildren().size() != 0
8911 )
8912 return setError(E_FAIL,
8913 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8914 "because it has %d differencing child hard disks"),
8915 medium->getLocationFull().c_str(),
8916 dev.uuid.raw(),
8917 mUserData->s.strName.c_str(),
8918 mData->m_strConfigFileFull.c_str(),
8919 medium->getChildren().size());
8920
8921 if (findAttachment(mMediaData->mAttachments,
8922 medium))
8923 return setError(E_FAIL,
8924 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8925 medium->getLocationFull().c_str(),
8926 dev.uuid.raw(),
8927 mUserData->s.strName.c_str(),
8928 mData->m_strConfigFileFull.c_str());
8929
8930 break;
8931 }
8932
8933 default:
8934 return setError(E_FAIL,
8935 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8936 medium->getLocationFull().c_str(),
8937 mUserData->s.strName.c_str(),
8938 mData->m_strConfigFileFull.c_str());
8939 }
8940
8941 if (FAILED(rc))
8942 break;
8943
8944 /* Bandwidth groups are loaded at this point. */
8945 ComObjPtr<BandwidthGroup> pBwGroup;
8946
8947 if (!dev.strBwGroup.isEmpty())
8948 {
8949 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8950 if (FAILED(rc))
8951 return setError(E_FAIL,
8952 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8953 medium->getLocationFull().c_str(),
8954 dev.strBwGroup.c_str(),
8955 mUserData->s.strName.c_str(),
8956 mData->m_strConfigFileFull.c_str());
8957 pBwGroup->reference();
8958 }
8959
8960 const Bstr controllerName = aStorageController->getName();
8961 ComObjPtr<MediumAttachment> pAttachment;
8962 pAttachment.createObject();
8963 rc = pAttachment->init(this,
8964 medium,
8965 controllerName,
8966 dev.lPort,
8967 dev.lDevice,
8968 dev.deviceType,
8969 false,
8970 dev.fPassThrough,
8971 dev.fTempEject,
8972 dev.fNonRotational,
8973 dev.fDiscard,
8974 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8975 if (FAILED(rc)) break;
8976
8977 /* associate the medium with this machine and snapshot */
8978 if (!medium.isNull())
8979 {
8980 AutoCaller medCaller(medium);
8981 if (FAILED(medCaller.rc())) return medCaller.rc();
8982 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8983
8984 if (isSnapshotMachine())
8985 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8986 else
8987 rc = medium->addBackReference(mData->mUuid);
8988 /* If the medium->addBackReference fails it sets an appropriate
8989 * error message, so no need to do any guesswork here. */
8990
8991 if (puuidRegistry)
8992 // caller wants registry ID to be set on all attached media (OVF import case)
8993 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8994 }
8995
8996 if (FAILED(rc))
8997 break;
8998
8999 /* back up mMediaData to let registeredInit() properly rollback on failure
9000 * (= limited accessibility) */
9001 setModified(IsModified_Storage);
9002 mMediaData.backup();
9003 mMediaData->mAttachments.push_back(pAttachment);
9004 }
9005
9006 return rc;
9007}
9008
9009/**
9010 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9011 *
9012 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9013 * @param aSnapshot where to return the found snapshot
9014 * @param aSetError true to set extended error info on failure
9015 */
9016HRESULT Machine::findSnapshotById(const Guid &aId,
9017 ComObjPtr<Snapshot> &aSnapshot,
9018 bool aSetError /* = false */)
9019{
9020 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9021
9022 if (!mData->mFirstSnapshot)
9023 {
9024 if (aSetError)
9025 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9026 return E_FAIL;
9027 }
9028
9029 if (aId.isEmpty())
9030 aSnapshot = mData->mFirstSnapshot;
9031 else
9032 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9033
9034 if (!aSnapshot)
9035 {
9036 if (aSetError)
9037 return setError(E_FAIL,
9038 tr("Could not find a snapshot with UUID {%s}"),
9039 aId.toString().c_str());
9040 return E_FAIL;
9041 }
9042
9043 return S_OK;
9044}
9045
9046/**
9047 * Returns the snapshot with the given name or fails of no such snapshot.
9048 *
9049 * @param aName snapshot name to find
9050 * @param aSnapshot where to return the found snapshot
9051 * @param aSetError true to set extended error info on failure
9052 */
9053HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9054 ComObjPtr<Snapshot> &aSnapshot,
9055 bool aSetError /* = false */)
9056{
9057 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9058
9059 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9060
9061 if (!mData->mFirstSnapshot)
9062 {
9063 if (aSetError)
9064 return setError(VBOX_E_OBJECT_NOT_FOUND,
9065 tr("This machine does not have any snapshots"));
9066 return VBOX_E_OBJECT_NOT_FOUND;
9067 }
9068
9069 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9070
9071 if (!aSnapshot)
9072 {
9073 if (aSetError)
9074 return setError(VBOX_E_OBJECT_NOT_FOUND,
9075 tr("Could not find a snapshot named '%s'"), strName.c_str());
9076 return VBOX_E_OBJECT_NOT_FOUND;
9077 }
9078
9079 return S_OK;
9080}
9081
9082/**
9083 * Returns a storage controller object with the given name.
9084 *
9085 * @param aName storage controller name to find
9086 * @param aStorageController where to return the found storage controller
9087 * @param aSetError true to set extended error info on failure
9088 */
9089HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9090 ComObjPtr<StorageController> &aStorageController,
9091 bool aSetError /* = false */)
9092{
9093 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9094
9095 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9096 it != mStorageControllers->end();
9097 ++it)
9098 {
9099 if ((*it)->getName() == aName)
9100 {
9101 aStorageController = (*it);
9102 return S_OK;
9103 }
9104 }
9105
9106 if (aSetError)
9107 return setError(VBOX_E_OBJECT_NOT_FOUND,
9108 tr("Could not find a storage controller named '%s'"),
9109 aName.c_str());
9110 return VBOX_E_OBJECT_NOT_FOUND;
9111}
9112
9113HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9114 MediaData::AttachmentList &atts)
9115{
9116 AutoCaller autoCaller(this);
9117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9118
9119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9120
9121 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9122 it != mMediaData->mAttachments.end();
9123 ++it)
9124 {
9125 const ComObjPtr<MediumAttachment> &pAtt = *it;
9126
9127 // should never happen, but deal with NULL pointers in the list.
9128 AssertStmt(!pAtt.isNull(), continue);
9129
9130 // getControllerName() needs caller+read lock
9131 AutoCaller autoAttCaller(pAtt);
9132 if (FAILED(autoAttCaller.rc()))
9133 {
9134 atts.clear();
9135 return autoAttCaller.rc();
9136 }
9137 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9138
9139 if (pAtt->getControllerName() == aName)
9140 atts.push_back(pAtt);
9141 }
9142
9143 return S_OK;
9144}
9145
9146/**
9147 * Helper for #saveSettings. Cares about renaming the settings directory and
9148 * file if the machine name was changed and about creating a new settings file
9149 * if this is a new machine.
9150 *
9151 * @note Must be never called directly but only from #saveSettings().
9152 */
9153HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9154{
9155 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9156
9157 HRESULT rc = S_OK;
9158
9159 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9160
9161 /// @todo need to handle primary group change, too
9162
9163 /* attempt to rename the settings file if machine name is changed */
9164 if ( mUserData->s.fNameSync
9165 && mUserData.isBackedUp()
9166 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9167 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9168 )
9169 {
9170 bool dirRenamed = false;
9171 bool fileRenamed = false;
9172
9173 Utf8Str configFile, newConfigFile;
9174 Utf8Str configFilePrev, newConfigFilePrev;
9175 Utf8Str configDir, newConfigDir;
9176
9177 do
9178 {
9179 int vrc = VINF_SUCCESS;
9180
9181 Utf8Str name = mUserData.backedUpData()->s.strName;
9182 Utf8Str newName = mUserData->s.strName;
9183 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9184 if (group == "/")
9185 group.setNull();
9186 Utf8Str newGroup = mUserData->s.llGroups.front();
9187 if (newGroup == "/")
9188 newGroup.setNull();
9189
9190 configFile = mData->m_strConfigFileFull;
9191
9192 /* first, rename the directory if it matches the group and machine name */
9193 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9194 group.c_str(), RTPATH_DELIMITER, name.c_str());
9195 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9196 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9197 configDir = configFile;
9198 configDir.stripFilename();
9199 newConfigDir = configDir;
9200 if ( configDir.length() >= groupPlusName.length()
9201 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9202 {
9203 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9204 Utf8Str newConfigBaseDir(newConfigDir);
9205 newConfigDir.append(newGroupPlusName);
9206 /* consistency: use \ if appropriate on the platform */
9207 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9208 /* new dir and old dir cannot be equal here because of 'if'
9209 * above and because name != newName */
9210 Assert(configDir != newConfigDir);
9211 if (!fSettingsFileIsNew)
9212 {
9213 /* perform real rename only if the machine is not new */
9214 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9215 if ( vrc == VERR_FILE_NOT_FOUND
9216 || vrc == VERR_PATH_NOT_FOUND)
9217 {
9218 /* create the parent directory, then retry renaming */
9219 Utf8Str parent(newConfigDir);
9220 parent.stripFilename();
9221 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9222 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9223 }
9224 if (RT_FAILURE(vrc))
9225 {
9226 rc = setError(E_FAIL,
9227 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9228 configDir.c_str(),
9229 newConfigDir.c_str(),
9230 vrc);
9231 break;
9232 }
9233 /* delete subdirectories which are no longer needed */
9234 Utf8Str dir(configDir);
9235 dir.stripFilename();
9236 while (dir != newConfigBaseDir && dir != ".")
9237 {
9238 vrc = RTDirRemove(dir.c_str());
9239 if (RT_FAILURE(vrc))
9240 break;
9241 dir.stripFilename();
9242 }
9243 dirRenamed = true;
9244 }
9245 }
9246
9247 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9248 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9249
9250 /* then try to rename the settings file itself */
9251 if (newConfigFile != configFile)
9252 {
9253 /* get the path to old settings file in renamed directory */
9254 configFile = Utf8StrFmt("%s%c%s",
9255 newConfigDir.c_str(),
9256 RTPATH_DELIMITER,
9257 RTPathFilename(configFile.c_str()));
9258 if (!fSettingsFileIsNew)
9259 {
9260 /* perform real rename only if the machine is not new */
9261 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9262 if (RT_FAILURE(vrc))
9263 {
9264 rc = setError(E_FAIL,
9265 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9266 configFile.c_str(),
9267 newConfigFile.c_str(),
9268 vrc);
9269 break;
9270 }
9271 fileRenamed = true;
9272 configFilePrev = configFile;
9273 configFilePrev += "-prev";
9274 newConfigFilePrev = newConfigFile;
9275 newConfigFilePrev += "-prev";
9276 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9277 }
9278 }
9279
9280 // update m_strConfigFileFull amd mConfigFile
9281 mData->m_strConfigFileFull = newConfigFile;
9282 // compute the relative path too
9283 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9284
9285 // store the old and new so that VirtualBox::saveSettings() can update
9286 // the media registry
9287 if ( mData->mRegistered
9288 && configDir != newConfigDir)
9289 {
9290 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9291
9292 if (pfNeedsGlobalSaveSettings)
9293 *pfNeedsGlobalSaveSettings = true;
9294 }
9295
9296 // in the saved state file path, replace the old directory with the new directory
9297 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9298 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9299
9300 // and do the same thing for the saved state file paths of all the online snapshots
9301 if (mData->mFirstSnapshot)
9302 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9303 newConfigDir.c_str());
9304 }
9305 while (0);
9306
9307 if (FAILED(rc))
9308 {
9309 /* silently try to rename everything back */
9310 if (fileRenamed)
9311 {
9312 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9313 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9314 }
9315 if (dirRenamed)
9316 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9317 }
9318
9319 if (FAILED(rc)) return rc;
9320 }
9321
9322 if (fSettingsFileIsNew)
9323 {
9324 /* create a virgin config file */
9325 int vrc = VINF_SUCCESS;
9326
9327 /* ensure the settings directory exists */
9328 Utf8Str path(mData->m_strConfigFileFull);
9329 path.stripFilename();
9330 if (!RTDirExists(path.c_str()))
9331 {
9332 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9333 if (RT_FAILURE(vrc))
9334 {
9335 return setError(E_FAIL,
9336 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9337 path.c_str(),
9338 vrc);
9339 }
9340 }
9341
9342 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9343 path = Utf8Str(mData->m_strConfigFileFull);
9344 RTFILE f = NIL_RTFILE;
9345 vrc = RTFileOpen(&f, path.c_str(),
9346 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9347 if (RT_FAILURE(vrc))
9348 return setError(E_FAIL,
9349 tr("Could not create the settings file '%s' (%Rrc)"),
9350 path.c_str(),
9351 vrc);
9352 RTFileClose(f);
9353 }
9354
9355 return rc;
9356}
9357
9358/**
9359 * Saves and commits machine data, user data and hardware data.
9360 *
9361 * Note that on failure, the data remains uncommitted.
9362 *
9363 * @a aFlags may combine the following flags:
9364 *
9365 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9366 * Used when saving settings after an operation that makes them 100%
9367 * correspond to the settings from the current snapshot.
9368 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9369 * #isReallyModified() returns false. This is necessary for cases when we
9370 * change machine data directly, not through the backup()/commit() mechanism.
9371 * - SaveS_Force: settings will be saved without doing a deep compare of the
9372 * settings structures. This is used when this is called because snapshots
9373 * have changed to avoid the overhead of the deep compare.
9374 *
9375 * @note Must be called from under this object's write lock. Locks children for
9376 * writing.
9377 *
9378 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9379 * initialized to false and that will be set to true by this function if
9380 * the caller must invoke VirtualBox::saveSettings() because the global
9381 * settings have changed. This will happen if a machine rename has been
9382 * saved and the global machine and media registries will therefore need
9383 * updating.
9384 */
9385HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9386 int aFlags /*= 0*/)
9387{
9388 LogFlowThisFuncEnter();
9389
9390 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9391
9392 /* make sure child objects are unable to modify the settings while we are
9393 * saving them */
9394 ensureNoStateDependencies();
9395
9396 AssertReturn(!isSnapshotMachine(),
9397 E_FAIL);
9398
9399 HRESULT rc = S_OK;
9400 bool fNeedsWrite = false;
9401
9402 /* First, prepare to save settings. It will care about renaming the
9403 * settings directory and file if the machine name was changed and about
9404 * creating a new settings file if this is a new machine. */
9405 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9406 if (FAILED(rc)) return rc;
9407
9408 // keep a pointer to the current settings structures
9409 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9410 settings::MachineConfigFile *pNewConfig = NULL;
9411
9412 try
9413 {
9414 // make a fresh one to have everyone write stuff into
9415 pNewConfig = new settings::MachineConfigFile(NULL);
9416 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9417
9418 // now go and copy all the settings data from COM to the settings structures
9419 // (this calles saveSettings() on all the COM objects in the machine)
9420 copyMachineDataToSettings(*pNewConfig);
9421
9422 if (aFlags & SaveS_ResetCurStateModified)
9423 {
9424 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9425 mData->mCurrentStateModified = FALSE;
9426 fNeedsWrite = true; // always, no need to compare
9427 }
9428 else if (aFlags & SaveS_Force)
9429 {
9430 fNeedsWrite = true; // always, no need to compare
9431 }
9432 else
9433 {
9434 if (!mData->mCurrentStateModified)
9435 {
9436 // do a deep compare of the settings that we just saved with the settings
9437 // previously stored in the config file; this invokes MachineConfigFile::operator==
9438 // which does a deep compare of all the settings, which is expensive but less expensive
9439 // than writing out XML in vain
9440 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9441
9442 // could still be modified if any settings changed
9443 mData->mCurrentStateModified = fAnySettingsChanged;
9444
9445 fNeedsWrite = fAnySettingsChanged;
9446 }
9447 else
9448 fNeedsWrite = true;
9449 }
9450
9451 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9452
9453 if (fNeedsWrite)
9454 // now spit it all out!
9455 pNewConfig->write(mData->m_strConfigFileFull);
9456
9457 mData->pMachineConfigFile = pNewConfig;
9458 delete pOldConfig;
9459 commit();
9460
9461 // after saving settings, we are no longer different from the XML on disk
9462 mData->flModifications = 0;
9463 }
9464 catch (HRESULT err)
9465 {
9466 // we assume that error info is set by the thrower
9467 rc = err;
9468
9469 // restore old config
9470 delete pNewConfig;
9471 mData->pMachineConfigFile = pOldConfig;
9472 }
9473 catch (...)
9474 {
9475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9476 }
9477
9478 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9479 {
9480 /* Fire the data change event, even on failure (since we've already
9481 * committed all data). This is done only for SessionMachines because
9482 * mutable Machine instances are always not registered (i.e. private
9483 * to the client process that creates them) and thus don't need to
9484 * inform callbacks. */
9485 if (isSessionMachine())
9486 mParent->onMachineDataChange(mData->mUuid);
9487 }
9488
9489 LogFlowThisFunc(("rc=%08X\n", rc));
9490 LogFlowThisFuncLeave();
9491 return rc;
9492}
9493
9494/**
9495 * Implementation for saving the machine settings into the given
9496 * settings::MachineConfigFile instance. This copies machine extradata
9497 * from the previous machine config file in the instance data, if any.
9498 *
9499 * This gets called from two locations:
9500 *
9501 * -- Machine::saveSettings(), during the regular XML writing;
9502 *
9503 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9504 * exported to OVF and we write the VirtualBox proprietary XML
9505 * into a <vbox:Machine> tag.
9506 *
9507 * This routine fills all the fields in there, including snapshots, *except*
9508 * for the following:
9509 *
9510 * -- fCurrentStateModified. There is some special logic associated with that.
9511 *
9512 * The caller can then call MachineConfigFile::write() or do something else
9513 * with it.
9514 *
9515 * Caller must hold the machine lock!
9516 *
9517 * This throws XML errors and HRESULT, so the caller must have a catch block!
9518 */
9519void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9520{
9521 // deep copy extradata
9522 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9523
9524 config.uuid = mData->mUuid;
9525
9526 // copy name, description, OS type, teleport, UTC etc.
9527 config.machineUserData = mUserData->s;
9528
9529 if ( mData->mMachineState == MachineState_Saved
9530 || mData->mMachineState == MachineState_Restoring
9531 // when deleting a snapshot we may or may not have a saved state in the current state,
9532 // so let's not assert here please
9533 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9534 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9535 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9536 && (!mSSData->strStateFilePath.isEmpty())
9537 )
9538 )
9539 {
9540 Assert(!mSSData->strStateFilePath.isEmpty());
9541 /* try to make the file name relative to the settings file dir */
9542 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9543 }
9544 else
9545 {
9546 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9547 config.strStateFile.setNull();
9548 }
9549
9550 if (mData->mCurrentSnapshot)
9551 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9552 else
9553 config.uuidCurrentSnapshot.clear();
9554
9555 config.timeLastStateChange = mData->mLastStateChange;
9556 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9557 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9558
9559 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9560 if (FAILED(rc)) throw rc;
9561
9562 rc = saveStorageControllers(config.storageMachine);
9563 if (FAILED(rc)) throw rc;
9564
9565 // save machine's media registry if this is VirtualBox 4.0 or later
9566 if (config.canHaveOwnMediaRegistry())
9567 {
9568 // determine machine folder
9569 Utf8Str strMachineFolder = getSettingsFileFull();
9570 strMachineFolder.stripFilename();
9571 mParent->saveMediaRegistry(config.mediaRegistry,
9572 getId(), // only media with registry ID == machine UUID
9573 strMachineFolder);
9574 // this throws HRESULT
9575 }
9576
9577 // save snapshots
9578 rc = saveAllSnapshots(config);
9579 if (FAILED(rc)) throw rc;
9580}
9581
9582/**
9583 * Saves all snapshots of the machine into the given machine config file. Called
9584 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9585 * @param config
9586 * @return
9587 */
9588HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9589{
9590 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9591
9592 HRESULT rc = S_OK;
9593
9594 try
9595 {
9596 config.llFirstSnapshot.clear();
9597
9598 if (mData->mFirstSnapshot)
9599 {
9600 settings::Snapshot snapNew;
9601 config.llFirstSnapshot.push_back(snapNew);
9602
9603 // get reference to the fresh copy of the snapshot on the list and
9604 // work on that copy directly to avoid excessive copying later
9605 settings::Snapshot &snap = config.llFirstSnapshot.front();
9606
9607 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9608 if (FAILED(rc)) throw rc;
9609 }
9610
9611// if (mType == IsSessionMachine)
9612// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9613
9614 }
9615 catch (HRESULT err)
9616 {
9617 /* we assume that error info is set by the thrower */
9618 rc = err;
9619 }
9620 catch (...)
9621 {
9622 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9623 }
9624
9625 return rc;
9626}
9627
9628/**
9629 * Saves the VM hardware configuration. It is assumed that the
9630 * given node is empty.
9631 *
9632 * @param data Reference to the settings object for the hardware config.
9633 * @param pDbg Pointer to the settings object for the debugging config
9634 * which happens to live in mHWData.
9635 * @param pAutostart Pointer to the settings object for the autostart config
9636 * which happens to live in mHWData.
9637 */
9638HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9639 settings::Autostart *pAutostart)
9640{
9641 HRESULT rc = S_OK;
9642
9643 try
9644 {
9645 /* The hardware version attribute (optional).
9646 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9647 if ( mHWData->mHWVersion == "1"
9648 && mSSData->strStateFilePath.isEmpty()
9649 )
9650 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. */
9651
9652 data.strVersion = mHWData->mHWVersion;
9653 data.uuid = mHWData->mHardwareUUID;
9654
9655 // CPU
9656 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9657 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9658 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9659 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9660 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9661 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9662 data.fPAE = !!mHWData->mPAEEnabled;
9663 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9664
9665 /* Standard and Extended CPUID leafs. */
9666 data.llCpuIdLeafs.clear();
9667 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9668 {
9669 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9670 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9671 }
9672 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9673 {
9674 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9675 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9676 }
9677
9678 data.cCPUs = mHWData->mCPUCount;
9679 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9680 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9681
9682 data.llCpus.clear();
9683 if (data.fCpuHotPlug)
9684 {
9685 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9686 {
9687 if (mHWData->mCPUAttached[idx])
9688 {
9689 settings::Cpu cpu;
9690 cpu.ulId = idx;
9691 data.llCpus.push_back(cpu);
9692 }
9693 }
9694 }
9695
9696 // memory
9697 data.ulMemorySizeMB = mHWData->mMemorySize;
9698 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9699
9700 // firmware
9701 data.firmwareType = mHWData->mFirmwareType;
9702
9703 // HID
9704 data.pointingHIDType = mHWData->mPointingHIDType;
9705 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9706
9707 // chipset
9708 data.chipsetType = mHWData->mChipsetType;
9709
9710 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9711
9712 // HPET
9713 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9714
9715 // boot order
9716 data.mapBootOrder.clear();
9717 for (size_t i = 0;
9718 i < RT_ELEMENTS(mHWData->mBootOrder);
9719 ++i)
9720 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9721
9722 // display
9723 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9724 data.cMonitors = mHWData->mMonitorCount;
9725 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9726 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9727 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9728 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9729 data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled;
9730 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9731
9732 /* VRDEServer settings (optional) */
9733 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9734 if (FAILED(rc)) throw rc;
9735
9736 /* BIOS (required) */
9737 rc = mBIOSSettings->saveSettings(data.biosSettings);
9738 if (FAILED(rc)) throw rc;
9739
9740 /* USB Controller (required) */
9741 rc = mUSBController->saveSettings(data.usbController);
9742 if (FAILED(rc)) throw rc;
9743
9744 /* Network adapters (required) */
9745 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9746 data.llNetworkAdapters.clear();
9747 /* Write out only the nominal number of network adapters for this
9748 * chipset type. Since Machine::commit() hasn't been called there
9749 * may be extra NIC settings in the vector. */
9750 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9751 {
9752 settings::NetworkAdapter nic;
9753 nic.ulSlot = slot;
9754 /* paranoia check... must not be NULL, but must not crash either. */
9755 if (mNetworkAdapters[slot])
9756 {
9757 rc = mNetworkAdapters[slot]->saveSettings(nic);
9758 if (FAILED(rc)) throw rc;
9759
9760 data.llNetworkAdapters.push_back(nic);
9761 }
9762 }
9763
9764 /* Serial ports */
9765 data.llSerialPorts.clear();
9766 for (ULONG slot = 0;
9767 slot < RT_ELEMENTS(mSerialPorts);
9768 ++slot)
9769 {
9770 settings::SerialPort s;
9771 s.ulSlot = slot;
9772 rc = mSerialPorts[slot]->saveSettings(s);
9773 if (FAILED(rc)) return rc;
9774
9775 data.llSerialPorts.push_back(s);
9776 }
9777
9778 /* Parallel ports */
9779 data.llParallelPorts.clear();
9780 for (ULONG slot = 0;
9781 slot < RT_ELEMENTS(mParallelPorts);
9782 ++slot)
9783 {
9784 settings::ParallelPort p;
9785 p.ulSlot = slot;
9786 rc = mParallelPorts[slot]->saveSettings(p);
9787 if (FAILED(rc)) return rc;
9788
9789 data.llParallelPorts.push_back(p);
9790 }
9791
9792 /* Audio adapter */
9793 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9794 if (FAILED(rc)) return rc;
9795
9796 /* Shared folders */
9797 data.llSharedFolders.clear();
9798 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9799 it != mHWData->mSharedFolders.end();
9800 ++it)
9801 {
9802 SharedFolder *pSF = *it;
9803 AutoCaller sfCaller(pSF);
9804 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9805 settings::SharedFolder sf;
9806 sf.strName = pSF->getName();
9807 sf.strHostPath = pSF->getHostPath();
9808 sf.fWritable = !!pSF->isWritable();
9809 sf.fAutoMount = !!pSF->isAutoMounted();
9810
9811 data.llSharedFolders.push_back(sf);
9812 }
9813
9814 // clipboard
9815 data.clipboardMode = mHWData->mClipboardMode;
9816
9817 // drag'n'drop
9818 data.dragAndDropMode = mHWData->mDragAndDropMode;
9819
9820 /* Guest */
9821 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9822
9823 // IO settings
9824 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9825 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9826
9827 /* BandwidthControl (required) */
9828 rc = mBandwidthControl->saveSettings(data.ioSettings);
9829 if (FAILED(rc)) throw rc;
9830
9831 /* Host PCI devices */
9832 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9833 it != mHWData->mPCIDeviceAssignments.end();
9834 ++it)
9835 {
9836 ComObjPtr<PCIDeviceAttachment> pda = *it;
9837 settings::HostPCIDeviceAttachment hpda;
9838
9839 rc = pda->saveSettings(hpda);
9840 if (FAILED(rc)) throw rc;
9841
9842 data.pciAttachments.push_back(hpda);
9843 }
9844
9845
9846 // guest properties
9847 data.llGuestProperties.clear();
9848#ifdef VBOX_WITH_GUEST_PROPS
9849 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9850 it != mHWData->mGuestProperties.end();
9851 ++it)
9852 {
9853 HWData::GuestProperty property = *it;
9854
9855 /* Remove transient guest properties at shutdown unless we
9856 * are saving state */
9857 if ( ( mData->mMachineState == MachineState_PoweredOff
9858 || mData->mMachineState == MachineState_Aborted
9859 || mData->mMachineState == MachineState_Teleported)
9860 && ( property.mFlags & guestProp::TRANSIENT
9861 || property.mFlags & guestProp::TRANSRESET))
9862 continue;
9863 settings::GuestProperty prop;
9864 prop.strName = property.strName;
9865 prop.strValue = property.strValue;
9866 prop.timestamp = property.mTimestamp;
9867 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9868 guestProp::writeFlags(property.mFlags, szFlags);
9869 prop.strFlags = szFlags;
9870
9871 data.llGuestProperties.push_back(prop);
9872 }
9873
9874 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9875 /* I presume this doesn't require a backup(). */
9876 mData->mGuestPropertiesModified = FALSE;
9877#endif /* VBOX_WITH_GUEST_PROPS defined */
9878
9879 *pDbg = mHWData->mDebugging;
9880 *pAutostart = mHWData->mAutostart;
9881 }
9882 catch(std::bad_alloc &)
9883 {
9884 return E_OUTOFMEMORY;
9885 }
9886
9887 AssertComRC(rc);
9888 return rc;
9889}
9890
9891/**
9892 * Saves the storage controller configuration.
9893 *
9894 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9895 */
9896HRESULT Machine::saveStorageControllers(settings::Storage &data)
9897{
9898 data.llStorageControllers.clear();
9899
9900 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9901 it != mStorageControllers->end();
9902 ++it)
9903 {
9904 HRESULT rc;
9905 ComObjPtr<StorageController> pCtl = *it;
9906
9907 settings::StorageController ctl;
9908 ctl.strName = pCtl->getName();
9909 ctl.controllerType = pCtl->getControllerType();
9910 ctl.storageBus = pCtl->getStorageBus();
9911 ctl.ulInstance = pCtl->getInstance();
9912 ctl.fBootable = pCtl->getBootable();
9913
9914 /* Save the port count. */
9915 ULONG portCount;
9916 rc = pCtl->COMGETTER(PortCount)(&portCount);
9917 ComAssertComRCRet(rc, rc);
9918 ctl.ulPortCount = portCount;
9919
9920 /* Save fUseHostIOCache */
9921 BOOL fUseHostIOCache;
9922 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9923 ComAssertComRCRet(rc, rc);
9924 ctl.fUseHostIOCache = !!fUseHostIOCache;
9925
9926 /* Save IDE emulation settings. */
9927 if (ctl.controllerType == StorageControllerType_IntelAhci)
9928 {
9929 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9930 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9931 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9932 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9933 )
9934 ComAssertComRCRet(rc, rc);
9935 }
9936
9937 /* save the devices now. */
9938 rc = saveStorageDevices(pCtl, ctl);
9939 ComAssertComRCRet(rc, rc);
9940
9941 data.llStorageControllers.push_back(ctl);
9942 }
9943
9944 return S_OK;
9945}
9946
9947/**
9948 * Saves the hard disk configuration.
9949 */
9950HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9951 settings::StorageController &data)
9952{
9953 MediaData::AttachmentList atts;
9954
9955 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9956 if (FAILED(rc)) return rc;
9957
9958 data.llAttachedDevices.clear();
9959 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9960 it != atts.end();
9961 ++it)
9962 {
9963 settings::AttachedDevice dev;
9964
9965 MediumAttachment *pAttach = *it;
9966 Medium *pMedium = pAttach->getMedium();
9967
9968 dev.deviceType = pAttach->getType();
9969 dev.lPort = pAttach->getPort();
9970 dev.lDevice = pAttach->getDevice();
9971 if (pMedium)
9972 {
9973 if (pMedium->isHostDrive())
9974 dev.strHostDriveSrc = pMedium->getLocationFull();
9975 else
9976 dev.uuid = pMedium->getId();
9977 dev.fPassThrough = pAttach->getPassthrough();
9978 dev.fTempEject = pAttach->getTempEject();
9979 dev.fDiscard = pAttach->getDiscard();
9980 }
9981
9982 dev.strBwGroup = pAttach->getBandwidthGroup();
9983
9984 data.llAttachedDevices.push_back(dev);
9985 }
9986
9987 return S_OK;
9988}
9989
9990/**
9991 * Saves machine state settings as defined by aFlags
9992 * (SaveSTS_* values).
9993 *
9994 * @param aFlags Combination of SaveSTS_* flags.
9995 *
9996 * @note Locks objects for writing.
9997 */
9998HRESULT Machine::saveStateSettings(int aFlags)
9999{
10000 if (aFlags == 0)
10001 return S_OK;
10002
10003 AutoCaller autoCaller(this);
10004 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10005
10006 /* This object's write lock is also necessary to serialize file access
10007 * (prevent concurrent reads and writes) */
10008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10009
10010 HRESULT rc = S_OK;
10011
10012 Assert(mData->pMachineConfigFile);
10013
10014 try
10015 {
10016 if (aFlags & SaveSTS_CurStateModified)
10017 mData->pMachineConfigFile->fCurrentStateModified = true;
10018
10019 if (aFlags & SaveSTS_StateFilePath)
10020 {
10021 if (!mSSData->strStateFilePath.isEmpty())
10022 /* try to make the file name relative to the settings file dir */
10023 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10024 else
10025 mData->pMachineConfigFile->strStateFile.setNull();
10026 }
10027
10028 if (aFlags & SaveSTS_StateTimeStamp)
10029 {
10030 Assert( mData->mMachineState != MachineState_Aborted
10031 || mSSData->strStateFilePath.isEmpty());
10032
10033 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10034
10035 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10036//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10037 }
10038
10039 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10040 }
10041 catch (...)
10042 {
10043 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10044 }
10045
10046 return rc;
10047}
10048
10049/**
10050 * Ensures that the given medium is added to a media registry. If this machine
10051 * was created with 4.0 or later, then the machine registry is used. Otherwise
10052 * the global VirtualBox media registry is used.
10053 *
10054 * Caller must NOT hold machine lock, media tree or any medium locks!
10055 *
10056 * @param pMedium
10057 */
10058void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10059{
10060 /* Paranoia checks: do not hold machine or media tree locks. */
10061 AssertReturnVoid(!isWriteLockOnCurrentThread());
10062 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10063
10064 ComObjPtr<Medium> pBase;
10065 {
10066 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10067 pBase = pMedium->getBase();
10068 }
10069
10070 /* Paranoia checks: do not hold medium locks. */
10071 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10072 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10073
10074 // decide which medium registry to use now that the medium is attached:
10075 Guid uuid;
10076 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10077 // machine XML is VirtualBox 4.0 or higher:
10078 uuid = getId(); // machine UUID
10079 else
10080 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10081
10082 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10083 mParent->markRegistryModified(uuid);
10084
10085 /* For more complex hard disk structures it can happen that the base
10086 * medium isn't yet associated with any medium registry. Do that now. */
10087 if (pMedium != pBase)
10088 {
10089 if (pBase->addRegistry(uuid, true /* fRecurse */))
10090 mParent->markRegistryModified(uuid);
10091 }
10092}
10093
10094/**
10095 * Creates differencing hard disks for all normal hard disks attached to this
10096 * machine and a new set of attachments to refer to created disks.
10097 *
10098 * Used when taking a snapshot or when deleting the current state. Gets called
10099 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10100 *
10101 * This method assumes that mMediaData contains the original hard disk attachments
10102 * it needs to create diffs for. On success, these attachments will be replaced
10103 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10104 * called to delete created diffs which will also rollback mMediaData and restore
10105 * whatever was backed up before calling this method.
10106 *
10107 * Attachments with non-normal hard disks are left as is.
10108 *
10109 * If @a aOnline is @c false then the original hard disks that require implicit
10110 * diffs will be locked for reading. Otherwise it is assumed that they are
10111 * already locked for writing (when the VM was started). Note that in the latter
10112 * case it is responsibility of the caller to lock the newly created diffs for
10113 * writing if this method succeeds.
10114 *
10115 * @param aProgress Progress object to run (must contain at least as
10116 * many operations left as the number of hard disks
10117 * attached).
10118 * @param aOnline Whether the VM was online prior to this operation.
10119 *
10120 * @note The progress object is not marked as completed, neither on success nor
10121 * on failure. This is a responsibility of the caller.
10122 *
10123 * @note Locks this object for writing.
10124 */
10125HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10126 ULONG aWeight,
10127 bool aOnline)
10128{
10129 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10130
10131 AutoCaller autoCaller(this);
10132 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10133
10134 AutoMultiWriteLock2 alock(this->lockHandle(),
10135 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10136
10137 /* must be in a protective state because we release the lock below */
10138 AssertReturn( mData->mMachineState == MachineState_Saving
10139 || mData->mMachineState == MachineState_LiveSnapshotting
10140 || mData->mMachineState == MachineState_RestoringSnapshot
10141 || mData->mMachineState == MachineState_DeletingSnapshot
10142 , E_FAIL);
10143
10144 HRESULT rc = S_OK;
10145
10146 MediumLockListMap lockedMediaOffline;
10147 MediumLockListMap *lockedMediaMap;
10148 if (aOnline)
10149 lockedMediaMap = &mData->mSession.mLockedMedia;
10150 else
10151 lockedMediaMap = &lockedMediaOffline;
10152
10153 try
10154 {
10155 if (!aOnline)
10156 {
10157 /* lock all attached hard disks early to detect "in use"
10158 * situations before creating actual diffs */
10159 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10160 it != mMediaData->mAttachments.end();
10161 ++it)
10162 {
10163 MediumAttachment* pAtt = *it;
10164 if (pAtt->getType() == DeviceType_HardDisk)
10165 {
10166 Medium* pMedium = pAtt->getMedium();
10167 Assert(pMedium);
10168
10169 MediumLockList *pMediumLockList(new MediumLockList());
10170 alock.release();
10171 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10172 false /* fMediumLockWrite */,
10173 NULL,
10174 *pMediumLockList);
10175 alock.acquire();
10176 if (FAILED(rc))
10177 {
10178 delete pMediumLockList;
10179 throw rc;
10180 }
10181 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10182 if (FAILED(rc))
10183 {
10184 throw setError(rc,
10185 tr("Collecting locking information for all attached media failed"));
10186 }
10187 }
10188 }
10189
10190 /* Now lock all media. If this fails, nothing is locked. */
10191 alock.release();
10192 rc = lockedMediaMap->Lock();
10193 alock.acquire();
10194 if (FAILED(rc))
10195 {
10196 throw setError(rc,
10197 tr("Locking of attached media failed"));
10198 }
10199 }
10200
10201 /* remember the current list (note that we don't use backup() since
10202 * mMediaData may be already backed up) */
10203 MediaData::AttachmentList atts = mMediaData->mAttachments;
10204
10205 /* start from scratch */
10206 mMediaData->mAttachments.clear();
10207
10208 /* go through remembered attachments and create diffs for normal hard
10209 * disks and attach them */
10210 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10211 it != atts.end();
10212 ++it)
10213 {
10214 MediumAttachment* pAtt = *it;
10215
10216 DeviceType_T devType = pAtt->getType();
10217 Medium* pMedium = pAtt->getMedium();
10218
10219 if ( devType != DeviceType_HardDisk
10220 || pMedium == NULL
10221 || pMedium->getType() != MediumType_Normal)
10222 {
10223 /* copy the attachment as is */
10224
10225 /** @todo the progress object created in Console::TakeSnaphot
10226 * only expects operations for hard disks. Later other
10227 * device types need to show up in the progress as well. */
10228 if (devType == DeviceType_HardDisk)
10229 {
10230 if (pMedium == NULL)
10231 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10232 aWeight); // weight
10233 else
10234 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10235 pMedium->getBase()->getName().c_str()).raw(),
10236 aWeight); // weight
10237 }
10238
10239 mMediaData->mAttachments.push_back(pAtt);
10240 continue;
10241 }
10242
10243 /* need a diff */
10244 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10245 pMedium->getBase()->getName().c_str()).raw(),
10246 aWeight); // weight
10247
10248 Utf8Str strFullSnapshotFolder;
10249 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10250
10251 ComObjPtr<Medium> diff;
10252 diff.createObject();
10253 // store the diff in the same registry as the parent
10254 // (this cannot fail here because we can't create implicit diffs for
10255 // unregistered images)
10256 Guid uuidRegistryParent;
10257 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10258 Assert(fInRegistry); NOREF(fInRegistry);
10259 rc = diff->init(mParent,
10260 pMedium->getPreferredDiffFormat(),
10261 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10262 uuidRegistryParent);
10263 if (FAILED(rc)) throw rc;
10264
10265 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10266 * the push_back? Looks like we're going to release medium with the
10267 * wrong kind of lock (general issue with if we fail anywhere at all)
10268 * and an orphaned VDI in the snapshots folder. */
10269
10270 /* update the appropriate lock list */
10271 MediumLockList *pMediumLockList;
10272 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10273 AssertComRCThrowRC(rc);
10274 if (aOnline)
10275 {
10276 alock.release();
10277 rc = pMediumLockList->Update(pMedium, false);
10278 alock.acquire();
10279 AssertComRCThrowRC(rc);
10280 }
10281
10282 /* release the locks before the potentially lengthy operation */
10283 alock.release();
10284 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10285 pMediumLockList,
10286 NULL /* aProgress */,
10287 true /* aWait */);
10288 alock.acquire();
10289 if (FAILED(rc)) throw rc;
10290
10291 rc = lockedMediaMap->Unlock();
10292 AssertComRCThrowRC(rc);
10293 alock.release();
10294 rc = pMediumLockList->Append(diff, true);
10295 alock.acquire();
10296 AssertComRCThrowRC(rc);
10297 alock.release();
10298 rc = lockedMediaMap->Lock();
10299 alock.acquire();
10300 AssertComRCThrowRC(rc);
10301
10302 rc = diff->addBackReference(mData->mUuid);
10303 AssertComRCThrowRC(rc);
10304
10305 /* add a new attachment */
10306 ComObjPtr<MediumAttachment> attachment;
10307 attachment.createObject();
10308 rc = attachment->init(this,
10309 diff,
10310 pAtt->getControllerName(),
10311 pAtt->getPort(),
10312 pAtt->getDevice(),
10313 DeviceType_HardDisk,
10314 true /* aImplicit */,
10315 false /* aPassthrough */,
10316 false /* aTempEject */,
10317 pAtt->getNonRotational(),
10318 pAtt->getDiscard(),
10319 pAtt->getBandwidthGroup());
10320 if (FAILED(rc)) throw rc;
10321
10322 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10323 AssertComRCThrowRC(rc);
10324 mMediaData->mAttachments.push_back(attachment);
10325 }
10326 }
10327 catch (HRESULT aRC) { rc = aRC; }
10328
10329 /* unlock all hard disks we locked */
10330 if (!aOnline)
10331 {
10332 ErrorInfoKeeper eik;
10333
10334 HRESULT rc1 = lockedMediaMap->Clear();
10335 AssertComRC(rc1);
10336 }
10337
10338 if (FAILED(rc))
10339 {
10340 MultiResult mrc = rc;
10341
10342 alock.release();
10343 mrc = deleteImplicitDiffs();
10344 }
10345
10346 return rc;
10347}
10348
10349/**
10350 * Deletes implicit differencing hard disks created either by
10351 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10352 *
10353 * Note that to delete hard disks created by #AttachDevice() this method is
10354 * called from #fixupMedia() when the changes are rolled back.
10355 *
10356 * @note Locks this object for writing.
10357 */
10358HRESULT Machine::deleteImplicitDiffs()
10359{
10360 AutoCaller autoCaller(this);
10361 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10362
10363 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10364 LogFlowThisFuncEnter();
10365
10366 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10367
10368 HRESULT rc = S_OK;
10369
10370 MediaData::AttachmentList implicitAtts;
10371
10372 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10373
10374 /* enumerate new attachments */
10375 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10376 it != mMediaData->mAttachments.end();
10377 ++it)
10378 {
10379 ComObjPtr<Medium> hd = (*it)->getMedium();
10380 if (hd.isNull())
10381 continue;
10382
10383 if ((*it)->isImplicit())
10384 {
10385 /* deassociate and mark for deletion */
10386 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10387 rc = hd->removeBackReference(mData->mUuid);
10388 AssertComRC(rc);
10389 implicitAtts.push_back(*it);
10390 continue;
10391 }
10392
10393 /* was this hard disk attached before? */
10394 if (!findAttachment(oldAtts, hd))
10395 {
10396 /* no: de-associate */
10397 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10398 rc = hd->removeBackReference(mData->mUuid);
10399 AssertComRC(rc);
10400 continue;
10401 }
10402 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10403 }
10404
10405 /* rollback hard disk changes */
10406 mMediaData.rollback();
10407
10408 MultiResult mrc(S_OK);
10409
10410 /* delete unused implicit diffs */
10411 if (implicitAtts.size() != 0)
10412 {
10413 /* will release the lock before the potentially lengthy
10414 * operation, so protect with the special state (unless already
10415 * protected) */
10416 MachineState_T oldState = mData->mMachineState;
10417 if ( oldState != MachineState_Saving
10418 && oldState != MachineState_LiveSnapshotting
10419 && oldState != MachineState_RestoringSnapshot
10420 && oldState != MachineState_DeletingSnapshot
10421 && oldState != MachineState_DeletingSnapshotOnline
10422 && oldState != MachineState_DeletingSnapshotPaused
10423 )
10424 setMachineState(MachineState_SettingUp);
10425
10426 alock.release();
10427
10428 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10429 it != implicitAtts.end();
10430 ++it)
10431 {
10432 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10433 ComObjPtr<Medium> hd = (*it)->getMedium();
10434
10435 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10436 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10437 mrc = rc;
10438 }
10439
10440 alock.acquire();
10441
10442 if (mData->mMachineState == MachineState_SettingUp)
10443 setMachineState(oldState);
10444 }
10445
10446 return mrc;
10447}
10448
10449/**
10450 * Looks through the given list of media attachments for one with the given parameters
10451 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10452 * can be searched as well if needed.
10453 *
10454 * @param list
10455 * @param aControllerName
10456 * @param aControllerPort
10457 * @param aDevice
10458 * @return
10459 */
10460MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10461 IN_BSTR aControllerName,
10462 LONG aControllerPort,
10463 LONG aDevice)
10464{
10465 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10466 it != ll.end();
10467 ++it)
10468 {
10469 MediumAttachment *pAttach = *it;
10470 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10471 return pAttach;
10472 }
10473
10474 return NULL;
10475}
10476
10477/**
10478 * Looks through the given list of media attachments for one with the given parameters
10479 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10480 * can be searched as well if needed.
10481 *
10482 * @param list
10483 * @param aControllerName
10484 * @param aControllerPort
10485 * @param aDevice
10486 * @return
10487 */
10488MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10489 ComObjPtr<Medium> pMedium)
10490{
10491 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10492 it != ll.end();
10493 ++it)
10494 {
10495 MediumAttachment *pAttach = *it;
10496 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10497 if (pMediumThis == pMedium)
10498 return pAttach;
10499 }
10500
10501 return NULL;
10502}
10503
10504/**
10505 * Looks through the given list of media attachments for one with the given parameters
10506 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10507 * can be searched as well if needed.
10508 *
10509 * @param list
10510 * @param aControllerName
10511 * @param aControllerPort
10512 * @param aDevice
10513 * @return
10514 */
10515MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10516 Guid &id)
10517{
10518 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10519 it != ll.end();
10520 ++it)
10521 {
10522 MediumAttachment *pAttach = *it;
10523 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10524 if (pMediumThis->getId() == id)
10525 return pAttach;
10526 }
10527
10528 return NULL;
10529}
10530
10531/**
10532 * Main implementation for Machine::DetachDevice. This also gets called
10533 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10534 *
10535 * @param pAttach Medium attachment to detach.
10536 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10537 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10538 * @return
10539 */
10540HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10541 AutoWriteLock &writeLock,
10542 Snapshot *pSnapshot)
10543{
10544 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10545 DeviceType_T mediumType = pAttach->getType();
10546
10547 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10548
10549 if (pAttach->isImplicit())
10550 {
10551 /* attempt to implicitly delete the implicitly created diff */
10552
10553 /// @todo move the implicit flag from MediumAttachment to Medium
10554 /// and forbid any hard disk operation when it is implicit. Or maybe
10555 /// a special media state for it to make it even more simple.
10556
10557 Assert(mMediaData.isBackedUp());
10558
10559 /* will release the lock before the potentially lengthy operation, so
10560 * protect with the special state */
10561 MachineState_T oldState = mData->mMachineState;
10562 setMachineState(MachineState_SettingUp);
10563
10564 writeLock.release();
10565
10566 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10567 true /*aWait*/);
10568
10569 writeLock.acquire();
10570
10571 setMachineState(oldState);
10572
10573 if (FAILED(rc)) return rc;
10574 }
10575
10576 setModified(IsModified_Storage);
10577 mMediaData.backup();
10578 mMediaData->mAttachments.remove(pAttach);
10579
10580 if (!oldmedium.isNull())
10581 {
10582 // if this is from a snapshot, do not defer detachment to commitMedia()
10583 if (pSnapshot)
10584 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10585 // else if non-hard disk media, do not defer detachment to commitMedia() either
10586 else if (mediumType != DeviceType_HardDisk)
10587 oldmedium->removeBackReference(mData->mUuid);
10588 }
10589
10590 return S_OK;
10591}
10592
10593/**
10594 * Goes thru all media of the given list and
10595 *
10596 * 1) calls detachDevice() on each of them for this machine and
10597 * 2) adds all Medium objects found in the process to the given list,
10598 * depending on cleanupMode.
10599 *
10600 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10601 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10602 * media to the list.
10603 *
10604 * This gets called from Machine::Unregister, both for the actual Machine and
10605 * the SnapshotMachine objects that might be found in the snapshots.
10606 *
10607 * Requires caller and locking. The machine lock must be passed in because it
10608 * will be passed on to detachDevice which needs it for temporary unlocking.
10609 *
10610 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10611 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10612 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10613 * otherwise no media get added.
10614 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10615 * @return
10616 */
10617HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10618 Snapshot *pSnapshot,
10619 CleanupMode_T cleanupMode,
10620 MediaList &llMedia)
10621{
10622 Assert(isWriteLockOnCurrentThread());
10623
10624 HRESULT rc;
10625
10626 // make a temporary list because detachDevice invalidates iterators into
10627 // mMediaData->mAttachments
10628 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10629
10630 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10631 it != llAttachments2.end();
10632 ++it)
10633 {
10634 ComObjPtr<MediumAttachment> &pAttach = *it;
10635 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10636
10637 if (!pMedium.isNull())
10638 {
10639 AutoCaller mac(pMedium);
10640 if (FAILED(mac.rc())) return mac.rc();
10641 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10642 DeviceType_T devType = pMedium->getDeviceType();
10643 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10644 && devType == DeviceType_HardDisk)
10645 || (cleanupMode == CleanupMode_Full)
10646 )
10647 {
10648 llMedia.push_back(pMedium);
10649 ComObjPtr<Medium> pParent = pMedium->getParent();
10650 /*
10651 * Search for medias which are not attached to any machine, but
10652 * in the chain to an attached disk. Mediums are only consided
10653 * if they are:
10654 * - have only one child
10655 * - no references to any machines
10656 * - are of normal medium type
10657 */
10658 while (!pParent.isNull())
10659 {
10660 AutoCaller mac1(pParent);
10661 if (FAILED(mac1.rc())) return mac1.rc();
10662 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10663 if (pParent->getChildren().size() == 1)
10664 {
10665 if ( pParent->getMachineBackRefCount() == 0
10666 && pParent->getType() == MediumType_Normal
10667 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10668 llMedia.push_back(pParent);
10669 }
10670 else
10671 break;
10672 pParent = pParent->getParent();
10673 }
10674 }
10675 }
10676
10677 // real machine: then we need to use the proper method
10678 rc = detachDevice(pAttach, writeLock, pSnapshot);
10679
10680 if (FAILED(rc))
10681 return rc;
10682 }
10683
10684 return S_OK;
10685}
10686
10687/**
10688 * Perform deferred hard disk detachments.
10689 *
10690 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10691 * backed up).
10692 *
10693 * If @a aOnline is @c true then this method will also unlock the old hard disks
10694 * for which the new implicit diffs were created and will lock these new diffs for
10695 * writing.
10696 *
10697 * @param aOnline Whether the VM was online prior to this operation.
10698 *
10699 * @note Locks this object for writing!
10700 */
10701void Machine::commitMedia(bool aOnline /*= false*/)
10702{
10703 AutoCaller autoCaller(this);
10704 AssertComRCReturnVoid(autoCaller.rc());
10705
10706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10707
10708 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10709
10710 HRESULT rc = S_OK;
10711
10712 /* no attach/detach operations -- nothing to do */
10713 if (!mMediaData.isBackedUp())
10714 return;
10715
10716 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10717 bool fMediaNeedsLocking = false;
10718
10719 /* enumerate new attachments */
10720 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10721 it != mMediaData->mAttachments.end();
10722 ++it)
10723 {
10724 MediumAttachment *pAttach = *it;
10725
10726 pAttach->commit();
10727
10728 Medium* pMedium = pAttach->getMedium();
10729 bool fImplicit = pAttach->isImplicit();
10730
10731 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10732 (pMedium) ? pMedium->getName().c_str() : "NULL",
10733 fImplicit));
10734
10735 /** @todo convert all this Machine-based voodoo to MediumAttachment
10736 * based commit logic. */
10737 if (fImplicit)
10738 {
10739 /* convert implicit attachment to normal */
10740 pAttach->setImplicit(false);
10741
10742 if ( aOnline
10743 && pMedium
10744 && pAttach->getType() == DeviceType_HardDisk
10745 )
10746 {
10747 ComObjPtr<Medium> parent = pMedium->getParent();
10748 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10749
10750 /* update the appropriate lock list */
10751 MediumLockList *pMediumLockList;
10752 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10753 AssertComRC(rc);
10754 if (pMediumLockList)
10755 {
10756 /* unlock if there's a need to change the locking */
10757 if (!fMediaNeedsLocking)
10758 {
10759 rc = mData->mSession.mLockedMedia.Unlock();
10760 AssertComRC(rc);
10761 fMediaNeedsLocking = true;
10762 }
10763 rc = pMediumLockList->Update(parent, false);
10764 AssertComRC(rc);
10765 rc = pMediumLockList->Append(pMedium, true);
10766 AssertComRC(rc);
10767 }
10768 }
10769
10770 continue;
10771 }
10772
10773 if (pMedium)
10774 {
10775 /* was this medium attached before? */
10776 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10777 oldIt != oldAtts.end();
10778 ++oldIt)
10779 {
10780 MediumAttachment *pOldAttach = *oldIt;
10781 if (pOldAttach->getMedium() == pMedium)
10782 {
10783 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10784
10785 /* yes: remove from old to avoid de-association */
10786 oldAtts.erase(oldIt);
10787 break;
10788 }
10789 }
10790 }
10791 }
10792
10793 /* enumerate remaining old attachments and de-associate from the
10794 * current machine state */
10795 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10796 it != oldAtts.end();
10797 ++it)
10798 {
10799 MediumAttachment *pAttach = *it;
10800 Medium* pMedium = pAttach->getMedium();
10801
10802 /* Detach only hard disks, since DVD/floppy media is detached
10803 * instantly in MountMedium. */
10804 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10805 {
10806 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10807
10808 /* now de-associate from the current machine state */
10809 rc = pMedium->removeBackReference(mData->mUuid);
10810 AssertComRC(rc);
10811
10812 if (aOnline)
10813 {
10814 /* unlock since medium is not used anymore */
10815 MediumLockList *pMediumLockList;
10816 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10817 AssertComRC(rc);
10818 if (pMediumLockList)
10819 {
10820 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10821 AssertComRC(rc);
10822 }
10823 }
10824 }
10825 }
10826
10827 /* take media locks again so that the locking state is consistent */
10828 if (fMediaNeedsLocking)
10829 {
10830 Assert(aOnline);
10831 rc = mData->mSession.mLockedMedia.Lock();
10832 AssertComRC(rc);
10833 }
10834
10835 /* commit the hard disk changes */
10836 mMediaData.commit();
10837
10838 if (isSessionMachine())
10839 {
10840 /*
10841 * Update the parent machine to point to the new owner.
10842 * This is necessary because the stored parent will point to the
10843 * session machine otherwise and cause crashes or errors later
10844 * when the session machine gets invalid.
10845 */
10846 /** @todo Change the MediumAttachment class to behave like any other
10847 * class in this regard by creating peer MediumAttachment
10848 * objects for session machines and share the data with the peer
10849 * machine.
10850 */
10851 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10852 it != mMediaData->mAttachments.end();
10853 ++it)
10854 {
10855 (*it)->updateParentMachine(mPeer);
10856 }
10857
10858 /* attach new data to the primary machine and reshare it */
10859 mPeer->mMediaData.attach(mMediaData);
10860 }
10861
10862 return;
10863}
10864
10865/**
10866 * Perform deferred deletion of implicitly created diffs.
10867 *
10868 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10869 * backed up).
10870 *
10871 * @note Locks this object for writing!
10872 */
10873void Machine::rollbackMedia()
10874{
10875 AutoCaller autoCaller(this);
10876 AssertComRCReturnVoid (autoCaller.rc());
10877
10878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10879
10880 LogFlowThisFunc(("Entering\n"));
10881
10882 HRESULT rc = S_OK;
10883
10884 /* no attach/detach operations -- nothing to do */
10885 if (!mMediaData.isBackedUp())
10886 return;
10887
10888 /* enumerate new attachments */
10889 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10890 it != mMediaData->mAttachments.end();
10891 ++it)
10892 {
10893 MediumAttachment *pAttach = *it;
10894 /* Fix up the backrefs for DVD/floppy media. */
10895 if (pAttach->getType() != DeviceType_HardDisk)
10896 {
10897 Medium* pMedium = pAttach->getMedium();
10898 if (pMedium)
10899 {
10900 rc = pMedium->removeBackReference(mData->mUuid);
10901 AssertComRC(rc);
10902 }
10903 }
10904
10905 (*it)->rollback();
10906
10907 pAttach = *it;
10908 /* Fix up the backrefs for DVD/floppy media. */
10909 if (pAttach->getType() != DeviceType_HardDisk)
10910 {
10911 Medium* pMedium = pAttach->getMedium();
10912 if (pMedium)
10913 {
10914 rc = pMedium->addBackReference(mData->mUuid);
10915 AssertComRC(rc);
10916 }
10917 }
10918 }
10919
10920 /** @todo convert all this Machine-based voodoo to MediumAttachment
10921 * based rollback logic. */
10922 deleteImplicitDiffs();
10923
10924 return;
10925}
10926
10927/**
10928 * Returns true if the settings file is located in the directory named exactly
10929 * as the machine; this means, among other things, that the machine directory
10930 * should be auto-renamed.
10931 *
10932 * @param aSettingsDir if not NULL, the full machine settings file directory
10933 * name will be assigned there.
10934 *
10935 * @note Doesn't lock anything.
10936 * @note Not thread safe (must be called from this object's lock).
10937 */
10938bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10939{
10940 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10941 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10942 if (aSettingsDir)
10943 *aSettingsDir = strMachineDirName;
10944 strMachineDirName.stripPath(); // vmname
10945 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10946 strConfigFileOnly.stripPath() // vmname.vbox
10947 .stripExt(); // vmname
10948
10949 AssertReturn(!strMachineDirName.isEmpty(), false);
10950 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10951
10952 return strMachineDirName == strConfigFileOnly;
10953}
10954
10955/**
10956 * Discards all changes to machine settings.
10957 *
10958 * @param aNotify Whether to notify the direct session about changes or not.
10959 *
10960 * @note Locks objects for writing!
10961 */
10962void Machine::rollback(bool aNotify)
10963{
10964 AutoCaller autoCaller(this);
10965 AssertComRCReturn(autoCaller.rc(), (void)0);
10966
10967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10968
10969 if (!mStorageControllers.isNull())
10970 {
10971 if (mStorageControllers.isBackedUp())
10972 {
10973 /* unitialize all new devices (absent in the backed up list). */
10974 StorageControllerList::const_iterator it = mStorageControllers->begin();
10975 StorageControllerList *backedList = mStorageControllers.backedUpData();
10976 while (it != mStorageControllers->end())
10977 {
10978 if ( std::find(backedList->begin(), backedList->end(), *it)
10979 == backedList->end()
10980 )
10981 {
10982 (*it)->uninit();
10983 }
10984 ++it;
10985 }
10986
10987 /* restore the list */
10988 mStorageControllers.rollback();
10989 }
10990
10991 /* rollback any changes to devices after restoring the list */
10992 if (mData->flModifications & IsModified_Storage)
10993 {
10994 StorageControllerList::const_iterator it = mStorageControllers->begin();
10995 while (it != mStorageControllers->end())
10996 {
10997 (*it)->rollback();
10998 ++it;
10999 }
11000 }
11001 }
11002
11003 mUserData.rollback();
11004
11005 mHWData.rollback();
11006
11007 if (mData->flModifications & IsModified_Storage)
11008 rollbackMedia();
11009
11010 if (mBIOSSettings)
11011 mBIOSSettings->rollback();
11012
11013 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11014 mVRDEServer->rollback();
11015
11016 if (mAudioAdapter)
11017 mAudioAdapter->rollback();
11018
11019 if (mUSBController && (mData->flModifications & IsModified_USB))
11020 mUSBController->rollback();
11021
11022 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11023 mBandwidthControl->rollback();
11024
11025 if (!mHWData.isNull())
11026 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11027 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11028 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11029 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11030
11031 if (mData->flModifications & IsModified_NetworkAdapters)
11032 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11033 if ( mNetworkAdapters[slot]
11034 && mNetworkAdapters[slot]->isModified())
11035 {
11036 mNetworkAdapters[slot]->rollback();
11037 networkAdapters[slot] = mNetworkAdapters[slot];
11038 }
11039
11040 if (mData->flModifications & IsModified_SerialPorts)
11041 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11042 if ( mSerialPorts[slot]
11043 && mSerialPorts[slot]->isModified())
11044 {
11045 mSerialPorts[slot]->rollback();
11046 serialPorts[slot] = mSerialPorts[slot];
11047 }
11048
11049 if (mData->flModifications & IsModified_ParallelPorts)
11050 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11051 if ( mParallelPorts[slot]
11052 && mParallelPorts[slot]->isModified())
11053 {
11054 mParallelPorts[slot]->rollback();
11055 parallelPorts[slot] = mParallelPorts[slot];
11056 }
11057
11058 if (aNotify)
11059 {
11060 /* inform the direct session about changes */
11061
11062 ComObjPtr<Machine> that = this;
11063 uint32_t flModifications = mData->flModifications;
11064 alock.release();
11065
11066 if (flModifications & IsModified_SharedFolders)
11067 that->onSharedFolderChange();
11068
11069 if (flModifications & IsModified_VRDEServer)
11070 that->onVRDEServerChange(/* aRestart */ TRUE);
11071 if (flModifications & IsModified_USB)
11072 that->onUSBControllerChange();
11073
11074 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11075 if (networkAdapters[slot])
11076 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11077 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11078 if (serialPorts[slot])
11079 that->onSerialPortChange(serialPorts[slot]);
11080 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11081 if (parallelPorts[slot])
11082 that->onParallelPortChange(parallelPorts[slot]);
11083
11084 if (flModifications & IsModified_Storage)
11085 that->onStorageControllerChange();
11086
11087#if 0
11088 if (flModifications & IsModified_BandwidthControl)
11089 that->onBandwidthControlChange();
11090#endif
11091 }
11092}
11093
11094/**
11095 * Commits all the changes to machine settings.
11096 *
11097 * Note that this operation is supposed to never fail.
11098 *
11099 * @note Locks this object and children for writing.
11100 */
11101void Machine::commit()
11102{
11103 AutoCaller autoCaller(this);
11104 AssertComRCReturnVoid(autoCaller.rc());
11105
11106 AutoCaller peerCaller(mPeer);
11107 AssertComRCReturnVoid(peerCaller.rc());
11108
11109 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11110
11111 /*
11112 * use safe commit to ensure Snapshot machines (that share mUserData)
11113 * will still refer to a valid memory location
11114 */
11115 mUserData.commitCopy();
11116
11117 mHWData.commit();
11118
11119 if (mMediaData.isBackedUp())
11120 commitMedia();
11121
11122 mBIOSSettings->commit();
11123 mVRDEServer->commit();
11124 mAudioAdapter->commit();
11125 mUSBController->commit();
11126 mBandwidthControl->commit();
11127
11128 /* Since mNetworkAdapters is a list which might have been changed (resized)
11129 * without using the Backupable<> template we need to handle the copying
11130 * of the list entries manually, including the creation of peers for the
11131 * new objects. */
11132 bool commitNetworkAdapters = false;
11133 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11134 if (mPeer)
11135 {
11136 /* commit everything, even the ones which will go away */
11137 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11138 mNetworkAdapters[slot]->commit();
11139 /* copy over the new entries, creating a peer and uninit the original */
11140 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11141 for (size_t slot = 0; slot < newSize; slot++)
11142 {
11143 /* look if this adapter has a peer device */
11144 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11145 if (!peer)
11146 {
11147 /* no peer means the adapter is a newly created one;
11148 * create a peer owning data this data share it with */
11149 peer.createObject();
11150 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11151 }
11152 mPeer->mNetworkAdapters[slot] = peer;
11153 }
11154 /* uninit any no longer needed network adapters */
11155 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11156 mNetworkAdapters[slot]->uninit();
11157 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11158 {
11159 if (mPeer->mNetworkAdapters[slot])
11160 mPeer->mNetworkAdapters[slot]->uninit();
11161 }
11162 /* Keep the original network adapter count until this point, so that
11163 * discarding a chipset type change will not lose settings. */
11164 mNetworkAdapters.resize(newSize);
11165 mPeer->mNetworkAdapters.resize(newSize);
11166 }
11167 else
11168 {
11169 /* we have no peer (our parent is the newly created machine);
11170 * just commit changes to the network adapters */
11171 commitNetworkAdapters = true;
11172 }
11173 if (commitNetworkAdapters)
11174 {
11175 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11176 mNetworkAdapters[slot]->commit();
11177 }
11178
11179 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11180 mSerialPorts[slot]->commit();
11181 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11182 mParallelPorts[slot]->commit();
11183
11184 bool commitStorageControllers = false;
11185
11186 if (mStorageControllers.isBackedUp())
11187 {
11188 mStorageControllers.commit();
11189
11190 if (mPeer)
11191 {
11192 /* Commit all changes to new controllers (this will reshare data with
11193 * peers for those who have peers) */
11194 StorageControllerList *newList = new StorageControllerList();
11195 StorageControllerList::const_iterator it = mStorageControllers->begin();
11196 while (it != mStorageControllers->end())
11197 {
11198 (*it)->commit();
11199
11200 /* look if this controller has a peer device */
11201 ComObjPtr<StorageController> peer = (*it)->getPeer();
11202 if (!peer)
11203 {
11204 /* no peer means the device is a newly created one;
11205 * create a peer owning data this device share it with */
11206 peer.createObject();
11207 peer->init(mPeer, *it, true /* aReshare */);
11208 }
11209 else
11210 {
11211 /* remove peer from the old list */
11212 mPeer->mStorageControllers->remove(peer);
11213 }
11214 /* and add it to the new list */
11215 newList->push_back(peer);
11216
11217 ++it;
11218 }
11219
11220 /* uninit old peer's controllers that are left */
11221 it = mPeer->mStorageControllers->begin();
11222 while (it != mPeer->mStorageControllers->end())
11223 {
11224 (*it)->uninit();
11225 ++it;
11226 }
11227
11228 /* attach new list of controllers to our peer */
11229 mPeer->mStorageControllers.attach(newList);
11230 }
11231 else
11232 {
11233 /* we have no peer (our parent is the newly created machine);
11234 * just commit changes to devices */
11235 commitStorageControllers = true;
11236 }
11237 }
11238 else
11239 {
11240 /* the list of controllers itself is not changed,
11241 * just commit changes to controllers themselves */
11242 commitStorageControllers = true;
11243 }
11244
11245 if (commitStorageControllers)
11246 {
11247 StorageControllerList::const_iterator it = mStorageControllers->begin();
11248 while (it != mStorageControllers->end())
11249 {
11250 (*it)->commit();
11251 ++it;
11252 }
11253 }
11254
11255 if (isSessionMachine())
11256 {
11257 /* attach new data to the primary machine and reshare it */
11258 mPeer->mUserData.attach(mUserData);
11259 mPeer->mHWData.attach(mHWData);
11260 /* mMediaData is reshared by fixupMedia */
11261 // mPeer->mMediaData.attach(mMediaData);
11262 Assert(mPeer->mMediaData.data() == mMediaData.data());
11263 }
11264}
11265
11266/**
11267 * Copies all the hardware data from the given machine.
11268 *
11269 * Currently, only called when the VM is being restored from a snapshot. In
11270 * particular, this implies that the VM is not running during this method's
11271 * call.
11272 *
11273 * @note This method must be called from under this object's lock.
11274 *
11275 * @note This method doesn't call #commit(), so all data remains backed up and
11276 * unsaved.
11277 */
11278void Machine::copyFrom(Machine *aThat)
11279{
11280 AssertReturnVoid(!isSnapshotMachine());
11281 AssertReturnVoid(aThat->isSnapshotMachine());
11282
11283 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11284
11285 mHWData.assignCopy(aThat->mHWData);
11286
11287 // create copies of all shared folders (mHWData after attaching a copy
11288 // contains just references to original objects)
11289 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11290 it != mHWData->mSharedFolders.end();
11291 ++it)
11292 {
11293 ComObjPtr<SharedFolder> folder;
11294 folder.createObject();
11295 HRESULT rc = folder->initCopy(getMachine(), *it);
11296 AssertComRC(rc);
11297 *it = folder;
11298 }
11299
11300 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11301 mVRDEServer->copyFrom(aThat->mVRDEServer);
11302 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11303 mUSBController->copyFrom(aThat->mUSBController);
11304 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11305
11306 /* create private copies of all controllers */
11307 mStorageControllers.backup();
11308 mStorageControllers->clear();
11309 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11310 it != aThat->mStorageControllers->end();
11311 ++it)
11312 {
11313 ComObjPtr<StorageController> ctrl;
11314 ctrl.createObject();
11315 ctrl->initCopy(this, *it);
11316 mStorageControllers->push_back(ctrl);
11317 }
11318
11319 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11320 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11321 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11322 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11323 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11324 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11325 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11326}
11327
11328/**
11329 * Returns whether the given storage controller is hotplug capable.
11330 *
11331 * @returns true if the controller supports hotplugging
11332 * false otherwise.
11333 * @param enmCtrlType The controller type to check for.
11334 */
11335bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11336{
11337 switch (enmCtrlType)
11338 {
11339 case StorageControllerType_IntelAhci:
11340 return true;
11341 case StorageControllerType_LsiLogic:
11342 case StorageControllerType_LsiLogicSas:
11343 case StorageControllerType_BusLogic:
11344 case StorageControllerType_PIIX3:
11345 case StorageControllerType_PIIX4:
11346 case StorageControllerType_ICH6:
11347 case StorageControllerType_I82078:
11348 default:
11349 return false;
11350 }
11351}
11352
11353#ifdef VBOX_WITH_RESOURCE_USAGE_API
11354
11355void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11356{
11357 AssertReturnVoid(isWriteLockOnCurrentThread());
11358 AssertPtrReturnVoid(aCollector);
11359
11360 pm::CollectorHAL *hal = aCollector->getHAL();
11361 /* Create sub metrics */
11362 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11363 "Percentage of processor time spent in user mode by the VM process.");
11364 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11365 "Percentage of processor time spent in kernel mode by the VM process.");
11366 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11367 "Size of resident portion of VM process in memory.");
11368 /* Create and register base metrics */
11369 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11370 cpuLoadUser, cpuLoadKernel);
11371 aCollector->registerBaseMetric(cpuLoad);
11372 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11373 ramUsageUsed);
11374 aCollector->registerBaseMetric(ramUsage);
11375
11376 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11377 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11378 new pm::AggregateAvg()));
11379 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11380 new pm::AggregateMin()));
11381 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11382 new pm::AggregateMax()));
11383 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11384 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11385 new pm::AggregateAvg()));
11386 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11387 new pm::AggregateMin()));
11388 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11389 new pm::AggregateMax()));
11390
11391 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11392 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11393 new pm::AggregateAvg()));
11394 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11395 new pm::AggregateMin()));
11396 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11397 new pm::AggregateMax()));
11398
11399
11400 /* Guest metrics collector */
11401 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11402 aCollector->registerGuest(mCollectorGuest);
11403 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11404 this, __PRETTY_FUNCTION__, mCollectorGuest));
11405
11406 /* Create sub metrics */
11407 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11408 "Percentage of processor time spent in user mode as seen by the guest.");
11409 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11410 "Percentage of processor time spent in kernel mode as seen by the guest.");
11411 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11412 "Percentage of processor time spent idling as seen by the guest.");
11413
11414 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11415 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11416 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11417 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11418 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11419 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11420
11421 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11422
11423 /* Create and register base metrics */
11424 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11425 guestLoadUser, guestLoadKernel, guestLoadIdle);
11426 aCollector->registerBaseMetric(guestCpuLoad);
11427
11428 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11429 guestMemTotal, guestMemFree,
11430 guestMemBalloon, guestMemShared,
11431 guestMemCache, guestPagedTotal);
11432 aCollector->registerBaseMetric(guestCpuMem);
11433
11434 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11435 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11436 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11437 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11438
11439 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11440 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11441 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11442 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11443
11444 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11445 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11446 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11447 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11448
11449 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11450 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11451 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11452 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11453
11454 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11455 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11456 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11457 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11458
11459 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11460 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11461 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11462 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11463
11464 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11465 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11466 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11467 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11468
11469 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11470 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11471 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11472 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11473
11474 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11475 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11476 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11477 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11478}
11479
11480void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11481{
11482 AssertReturnVoid(isWriteLockOnCurrentThread());
11483
11484 if (aCollector)
11485 {
11486 aCollector->unregisterMetricsFor(aMachine);
11487 aCollector->unregisterBaseMetricsFor(aMachine);
11488 }
11489}
11490
11491#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11492
11493
11494////////////////////////////////////////////////////////////////////////////////
11495
11496DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11497
11498HRESULT SessionMachine::FinalConstruct()
11499{
11500 LogFlowThisFunc(("\n"));
11501
11502#if defined(RT_OS_WINDOWS)
11503 mIPCSem = NULL;
11504#elif defined(RT_OS_OS2)
11505 mIPCSem = NULLHANDLE;
11506#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11507 mIPCSem = -1;
11508#else
11509# error "Port me!"
11510#endif
11511
11512 return BaseFinalConstruct();
11513}
11514
11515void SessionMachine::FinalRelease()
11516{
11517 LogFlowThisFunc(("\n"));
11518
11519 uninit(Uninit::Unexpected);
11520
11521 BaseFinalRelease();
11522}
11523
11524/**
11525 * @note Must be called only by Machine::openSession() from its own write lock.
11526 */
11527HRESULT SessionMachine::init(Machine *aMachine)
11528{
11529 LogFlowThisFuncEnter();
11530 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11531
11532 AssertReturn(aMachine, E_INVALIDARG);
11533
11534 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11535
11536 /* Enclose the state transition NotReady->InInit->Ready */
11537 AutoInitSpan autoInitSpan(this);
11538 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11539
11540 /* create the interprocess semaphore */
11541#if defined(RT_OS_WINDOWS)
11542 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11543 for (size_t i = 0; i < mIPCSemName.length(); i++)
11544 if (mIPCSemName.raw()[i] == '\\')
11545 mIPCSemName.raw()[i] = '/';
11546 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11547 ComAssertMsgRet(mIPCSem,
11548 ("Cannot create IPC mutex '%ls', err=%d",
11549 mIPCSemName.raw(), ::GetLastError()),
11550 E_FAIL);
11551#elif defined(RT_OS_OS2)
11552 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11553 aMachine->mData->mUuid.raw());
11554 mIPCSemName = ipcSem;
11555 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11556 ComAssertMsgRet(arc == NO_ERROR,
11557 ("Cannot create IPC mutex '%s', arc=%ld",
11558 ipcSem.c_str(), arc),
11559 E_FAIL);
11560#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11561# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11562# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11563 /** @todo Check that this still works correctly. */
11564 AssertCompileSize(key_t, 8);
11565# else
11566 AssertCompileSize(key_t, 4);
11567# endif
11568 key_t key;
11569 mIPCSem = -1;
11570 mIPCKey = "0";
11571 for (uint32_t i = 0; i < 1 << 24; i++)
11572 {
11573 key = ((uint32_t)'V' << 24) | i;
11574 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11575 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11576 {
11577 mIPCSem = sem;
11578 if (sem >= 0)
11579 mIPCKey = BstrFmt("%u", key);
11580 break;
11581 }
11582 }
11583# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11584 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11585 char *pszSemName = NULL;
11586 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11587 key_t key = ::ftok(pszSemName, 'V');
11588 RTStrFree(pszSemName);
11589
11590 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11591# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11592
11593 int errnoSave = errno;
11594 if (mIPCSem < 0 && errnoSave == ENOSYS)
11595 {
11596 setError(E_FAIL,
11597 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11598 "support for SysV IPC. Check the host kernel configuration for "
11599 "CONFIG_SYSVIPC=y"));
11600 return E_FAIL;
11601 }
11602 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11603 * the IPC semaphores */
11604 if (mIPCSem < 0 && errnoSave == ENOSPC)
11605 {
11606#ifdef RT_OS_LINUX
11607 setError(E_FAIL,
11608 tr("Cannot create IPC semaphore because the system limit for the "
11609 "maximum number of semaphore sets (SEMMNI), or the system wide "
11610 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11611 "current set of SysV IPC semaphores can be determined from "
11612 "the file /proc/sysvipc/sem"));
11613#else
11614 setError(E_FAIL,
11615 tr("Cannot create IPC semaphore because the system-imposed limit "
11616 "on the maximum number of allowed semaphores or semaphore "
11617 "identifiers system-wide would be exceeded"));
11618#endif
11619 return E_FAIL;
11620 }
11621 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11622 E_FAIL);
11623 /* set the initial value to 1 */
11624 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11625 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11626 E_FAIL);
11627#else
11628# error "Port me!"
11629#endif
11630
11631 /* memorize the peer Machine */
11632 unconst(mPeer) = aMachine;
11633 /* share the parent pointer */
11634 unconst(mParent) = aMachine->mParent;
11635
11636 /* take the pointers to data to share */
11637 mData.share(aMachine->mData);
11638 mSSData.share(aMachine->mSSData);
11639
11640 mUserData.share(aMachine->mUserData);
11641 mHWData.share(aMachine->mHWData);
11642 mMediaData.share(aMachine->mMediaData);
11643
11644 mStorageControllers.allocate();
11645 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11646 it != aMachine->mStorageControllers->end();
11647 ++it)
11648 {
11649 ComObjPtr<StorageController> ctl;
11650 ctl.createObject();
11651 ctl->init(this, *it);
11652 mStorageControllers->push_back(ctl);
11653 }
11654
11655 unconst(mBIOSSettings).createObject();
11656 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11657 /* create another VRDEServer object that will be mutable */
11658 unconst(mVRDEServer).createObject();
11659 mVRDEServer->init(this, aMachine->mVRDEServer);
11660 /* create another audio adapter object that will be mutable */
11661 unconst(mAudioAdapter).createObject();
11662 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11663 /* create a list of serial ports that will be mutable */
11664 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11665 {
11666 unconst(mSerialPorts[slot]).createObject();
11667 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11668 }
11669 /* create a list of parallel ports that will be mutable */
11670 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11671 {
11672 unconst(mParallelPorts[slot]).createObject();
11673 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11674 }
11675 /* create another USB controller object that will be mutable */
11676 unconst(mUSBController).createObject();
11677 mUSBController->init(this, aMachine->mUSBController);
11678
11679 /* create a list of network adapters that will be mutable */
11680 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11681 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11682 {
11683 unconst(mNetworkAdapters[slot]).createObject();
11684 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11685 }
11686
11687 /* create another bandwidth control object that will be mutable */
11688 unconst(mBandwidthControl).createObject();
11689 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11690
11691 /* default is to delete saved state on Saved -> PoweredOff transition */
11692 mRemoveSavedState = true;
11693
11694 /* Confirm a successful initialization when it's the case */
11695 autoInitSpan.setSucceeded();
11696
11697 LogFlowThisFuncLeave();
11698 return S_OK;
11699}
11700
11701/**
11702 * Uninitializes this session object. If the reason is other than
11703 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11704 *
11705 * @param aReason uninitialization reason
11706 *
11707 * @note Locks mParent + this object for writing.
11708 */
11709void SessionMachine::uninit(Uninit::Reason aReason)
11710{
11711 LogFlowThisFuncEnter();
11712 LogFlowThisFunc(("reason=%d\n", aReason));
11713
11714 /*
11715 * Strongly reference ourselves to prevent this object deletion after
11716 * mData->mSession.mMachine.setNull() below (which can release the last
11717 * reference and call the destructor). Important: this must be done before
11718 * accessing any members (and before AutoUninitSpan that does it as well).
11719 * This self reference will be released as the very last step on return.
11720 */
11721 ComObjPtr<SessionMachine> selfRef = this;
11722
11723 /* Enclose the state transition Ready->InUninit->NotReady */
11724 AutoUninitSpan autoUninitSpan(this);
11725 if (autoUninitSpan.uninitDone())
11726 {
11727 LogFlowThisFunc(("Already uninitialized\n"));
11728 LogFlowThisFuncLeave();
11729 return;
11730 }
11731
11732 if (autoUninitSpan.initFailed())
11733 {
11734 /* We've been called by init() because it's failed. It's not really
11735 * necessary (nor it's safe) to perform the regular uninit sequence
11736 * below, the following is enough.
11737 */
11738 LogFlowThisFunc(("Initialization failed.\n"));
11739#if defined(RT_OS_WINDOWS)
11740 if (mIPCSem)
11741 ::CloseHandle(mIPCSem);
11742 mIPCSem = NULL;
11743#elif defined(RT_OS_OS2)
11744 if (mIPCSem != NULLHANDLE)
11745 ::DosCloseMutexSem(mIPCSem);
11746 mIPCSem = NULLHANDLE;
11747#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11748 if (mIPCSem >= 0)
11749 ::semctl(mIPCSem, 0, IPC_RMID);
11750 mIPCSem = -1;
11751# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11752 mIPCKey = "0";
11753# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11754#else
11755# error "Port me!"
11756#endif
11757 uninitDataAndChildObjects();
11758 mData.free();
11759 unconst(mParent) = NULL;
11760 unconst(mPeer) = NULL;
11761 LogFlowThisFuncLeave();
11762 return;
11763 }
11764
11765 MachineState_T lastState;
11766 {
11767 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11768 lastState = mData->mMachineState;
11769 }
11770 NOREF(lastState);
11771
11772#ifdef VBOX_WITH_USB
11773 // release all captured USB devices, but do this before requesting the locks below
11774 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11775 {
11776 /* Console::captureUSBDevices() is called in the VM process only after
11777 * setting the machine state to Starting or Restoring.
11778 * Console::detachAllUSBDevices() will be called upon successful
11779 * termination. So, we need to release USB devices only if there was
11780 * an abnormal termination of a running VM.
11781 *
11782 * This is identical to SessionMachine::DetachAllUSBDevices except
11783 * for the aAbnormal argument. */
11784 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11785 AssertComRC(rc);
11786 NOREF(rc);
11787
11788 USBProxyService *service = mParent->host()->usbProxyService();
11789 if (service)
11790 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11791 }
11792#endif /* VBOX_WITH_USB */
11793
11794 // we need to lock this object in uninit() because the lock is shared
11795 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11796 // and others need mParent lock, and USB needs host lock.
11797 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11798
11799#if 0
11800 // Trigger async cleanup tasks, avoid doing things here which are not
11801 // vital to be done immediately and maybe need more locks. This calls
11802 // Machine::unregisterMetrics().
11803 mParent->onMachineUninit(mPeer);
11804#else
11805 /*
11806 * It is safe to call Machine::unregisterMetrics() here because
11807 * PerformanceCollector::samplerCallback no longer accesses guest methods
11808 * holding the lock.
11809 */
11810 unregisterMetrics(mParent->performanceCollector(), mPeer);
11811#endif
11812 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11813 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11814 this, __PRETTY_FUNCTION__, mCollectorGuest));
11815 if (mCollectorGuest)
11816 {
11817 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11818 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11819 mCollectorGuest = NULL;
11820 }
11821
11822 if (aReason == Uninit::Abnormal)
11823 {
11824 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11825 Global::IsOnlineOrTransient(lastState)));
11826
11827 /* reset the state to Aborted */
11828 if (mData->mMachineState != MachineState_Aborted)
11829 setMachineState(MachineState_Aborted);
11830 }
11831
11832 // any machine settings modified?
11833 if (mData->flModifications)
11834 {
11835 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11836 rollback(false /* aNotify */);
11837 }
11838
11839 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11840 || !mConsoleTaskData.mSnapshot);
11841 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11842 {
11843 LogWarningThisFunc(("canceling failed save state request!\n"));
11844 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11845 }
11846 else if (!mConsoleTaskData.mSnapshot.isNull())
11847 {
11848 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11849
11850 /* delete all differencing hard disks created (this will also attach
11851 * their parents back by rolling back mMediaData) */
11852 rollbackMedia();
11853
11854 // delete the saved state file (it might have been already created)
11855 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11856 // think it's still in use
11857 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11858 mConsoleTaskData.mSnapshot->uninit();
11859 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11860 }
11861
11862 if (!mData->mSession.mType.isEmpty())
11863 {
11864 /* mType is not null when this machine's process has been started by
11865 * Machine::LaunchVMProcess(), therefore it is our child. We
11866 * need to queue the PID to reap the process (and avoid zombies on
11867 * Linux). */
11868 Assert(mData->mSession.mPID != NIL_RTPROCESS);
11869 mParent->addProcessToReap(mData->mSession.mPID);
11870 }
11871
11872 mData->mSession.mPID = NIL_RTPROCESS;
11873
11874 if (aReason == Uninit::Unexpected)
11875 {
11876 /* Uninitialization didn't come from #checkForDeath(), so tell the
11877 * client watcher thread to update the set of machines that have open
11878 * sessions. */
11879 mParent->updateClientWatcher();
11880 }
11881
11882 /* uninitialize all remote controls */
11883 if (mData->mSession.mRemoteControls.size())
11884 {
11885 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11886 mData->mSession.mRemoteControls.size()));
11887
11888 Data::Session::RemoteControlList::iterator it =
11889 mData->mSession.mRemoteControls.begin();
11890 while (it != mData->mSession.mRemoteControls.end())
11891 {
11892 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11893 HRESULT rc = (*it)->Uninitialize();
11894 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11895 if (FAILED(rc))
11896 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11897 ++it;
11898 }
11899 mData->mSession.mRemoteControls.clear();
11900 }
11901
11902 /*
11903 * An expected uninitialization can come only from #checkForDeath().
11904 * Otherwise it means that something's gone really wrong (for example,
11905 * the Session implementation has released the VirtualBox reference
11906 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11907 * etc). However, it's also possible, that the client releases the IPC
11908 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11909 * but the VirtualBox release event comes first to the server process.
11910 * This case is practically possible, so we should not assert on an
11911 * unexpected uninit, just log a warning.
11912 */
11913
11914 if ((aReason == Uninit::Unexpected))
11915 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11916
11917 if (aReason != Uninit::Normal)
11918 {
11919 mData->mSession.mDirectControl.setNull();
11920 }
11921 else
11922 {
11923 /* this must be null here (see #OnSessionEnd()) */
11924 Assert(mData->mSession.mDirectControl.isNull());
11925 Assert(mData->mSession.mState == SessionState_Unlocking);
11926 Assert(!mData->mSession.mProgress.isNull());
11927 }
11928 if (mData->mSession.mProgress)
11929 {
11930 if (aReason == Uninit::Normal)
11931 mData->mSession.mProgress->notifyComplete(S_OK);
11932 else
11933 mData->mSession.mProgress->notifyComplete(E_FAIL,
11934 COM_IIDOF(ISession),
11935 getComponentName(),
11936 tr("The VM session was aborted"));
11937 mData->mSession.mProgress.setNull();
11938 }
11939
11940 /* remove the association between the peer machine and this session machine */
11941 Assert( (SessionMachine*)mData->mSession.mMachine == this
11942 || aReason == Uninit::Unexpected);
11943
11944 /* reset the rest of session data */
11945 mData->mSession.mMachine.setNull();
11946 mData->mSession.mState = SessionState_Unlocked;
11947 mData->mSession.mType.setNull();
11948
11949 /* close the interprocess semaphore before leaving the exclusive lock */
11950#if defined(RT_OS_WINDOWS)
11951 if (mIPCSem)
11952 ::CloseHandle(mIPCSem);
11953 mIPCSem = NULL;
11954#elif defined(RT_OS_OS2)
11955 if (mIPCSem != NULLHANDLE)
11956 ::DosCloseMutexSem(mIPCSem);
11957 mIPCSem = NULLHANDLE;
11958#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11959 if (mIPCSem >= 0)
11960 ::semctl(mIPCSem, 0, IPC_RMID);
11961 mIPCSem = -1;
11962# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11963 mIPCKey = "0";
11964# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11965#else
11966# error "Port me!"
11967#endif
11968
11969 /* fire an event */
11970 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11971
11972 uninitDataAndChildObjects();
11973
11974 /* free the essential data structure last */
11975 mData.free();
11976
11977 /* release the exclusive lock before setting the below two to NULL */
11978 multilock.release();
11979
11980 unconst(mParent) = NULL;
11981 unconst(mPeer) = NULL;
11982
11983 LogFlowThisFuncLeave();
11984}
11985
11986// util::Lockable interface
11987////////////////////////////////////////////////////////////////////////////////
11988
11989/**
11990 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
11991 * with the primary Machine instance (mPeer).
11992 */
11993RWLockHandle *SessionMachine::lockHandle() const
11994{
11995 AssertReturn(mPeer != NULL, NULL);
11996 return mPeer->lockHandle();
11997}
11998
11999// IInternalMachineControl methods
12000////////////////////////////////////////////////////////////////////////////////
12001
12002/**
12003 * Passes collected guest statistics to performance collector object
12004 */
12005STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
12006 ULONG aCpuKernel, ULONG aCpuIdle,
12007 ULONG aMemTotal, ULONG aMemFree,
12008 ULONG aMemBalloon, ULONG aMemShared,
12009 ULONG aMemCache, ULONG aPageTotal,
12010 ULONG aAllocVMM, ULONG aFreeVMM,
12011 ULONG aBalloonedVMM, ULONG aSharedVMM)
12012{
12013 if (mCollectorGuest)
12014 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12015 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12016 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12017 aBalloonedVMM, aSharedVMM);
12018
12019 return S_OK;
12020}
12021
12022/**
12023 * @note Locks this object for writing.
12024 */
12025STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12026{
12027 AutoCaller autoCaller(this);
12028 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12029
12030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12031
12032 mRemoveSavedState = aRemove;
12033
12034 return S_OK;
12035}
12036
12037/**
12038 * @note Locks the same as #setMachineState() does.
12039 */
12040STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12041{
12042 return setMachineState(aMachineState);
12043}
12044
12045/**
12046 * @note Locks this object for reading.
12047 */
12048STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12049{
12050 AutoCaller autoCaller(this);
12051 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12052
12053 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12054
12055#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12056 mIPCSemName.cloneTo(aId);
12057 return S_OK;
12058#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12059# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12060 mIPCKey.cloneTo(aId);
12061# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12062 mData->m_strConfigFileFull.cloneTo(aId);
12063# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12064 return S_OK;
12065#else
12066# error "Port me!"
12067#endif
12068}
12069
12070/**
12071 * @note Locks this object for writing.
12072 */
12073STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12074{
12075 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12076 AutoCaller autoCaller(this);
12077 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12078
12079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12080
12081 if (mData->mSession.mState != SessionState_Locked)
12082 return VBOX_E_INVALID_OBJECT_STATE;
12083
12084 if (!mData->mSession.mProgress.isNull())
12085 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12086
12087 LogFlowThisFunc(("returns S_OK.\n"));
12088 return S_OK;
12089}
12090
12091/**
12092 * @note Locks this object for writing.
12093 */
12094STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12095{
12096 AutoCaller autoCaller(this);
12097 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12098
12099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12100
12101 if (mData->mSession.mState != SessionState_Locked)
12102 return VBOX_E_INVALID_OBJECT_STATE;
12103
12104 /* Finalize the LaunchVMProcess progress object. */
12105 if (mData->mSession.mProgress)
12106 {
12107 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12108 mData->mSession.mProgress.setNull();
12109 }
12110
12111 if (SUCCEEDED((HRESULT)iResult))
12112 {
12113#ifdef VBOX_WITH_RESOURCE_USAGE_API
12114 /* The VM has been powered up successfully, so it makes sense
12115 * now to offer the performance metrics for a running machine
12116 * object. Doing it earlier wouldn't be safe. */
12117 registerMetrics(mParent->performanceCollector(), mPeer,
12118 mData->mSession.mPID);
12119#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12120 }
12121
12122 return S_OK;
12123}
12124
12125/**
12126 * @note Locks this object for writing.
12127 */
12128STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12129{
12130 LogFlowThisFuncEnter();
12131
12132 CheckComArgOutPointerValid(aProgress);
12133
12134 AutoCaller autoCaller(this);
12135 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12136
12137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12138
12139 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12140 E_FAIL);
12141
12142 /* create a progress object to track operation completion */
12143 ComObjPtr<Progress> pProgress;
12144 pProgress.createObject();
12145 pProgress->init(getVirtualBox(),
12146 static_cast<IMachine *>(this) /* aInitiator */,
12147 Bstr(tr("Stopping the virtual machine")).raw(),
12148 FALSE /* aCancelable */);
12149
12150 /* fill in the console task data */
12151 mConsoleTaskData.mLastState = mData->mMachineState;
12152 mConsoleTaskData.mProgress = pProgress;
12153
12154 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12155 setMachineState(MachineState_Stopping);
12156
12157 pProgress.queryInterfaceTo(aProgress);
12158
12159 return S_OK;
12160}
12161
12162/**
12163 * @note Locks this object for writing.
12164 */
12165STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12166{
12167 LogFlowThisFuncEnter();
12168
12169 AutoCaller autoCaller(this);
12170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12171
12172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12173
12174 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12175 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12176 && mConsoleTaskData.mLastState != MachineState_Null,
12177 E_FAIL);
12178
12179 /*
12180 * On failure, set the state to the state we had when BeginPoweringDown()
12181 * was called (this is expected by Console::PowerDown() and the associated
12182 * task). On success the VM process already changed the state to
12183 * MachineState_PoweredOff, so no need to do anything.
12184 */
12185 if (FAILED(iResult))
12186 setMachineState(mConsoleTaskData.mLastState);
12187
12188 /* notify the progress object about operation completion */
12189 Assert(mConsoleTaskData.mProgress);
12190 if (SUCCEEDED(iResult))
12191 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12192 else
12193 {
12194 Utf8Str strErrMsg(aErrMsg);
12195 if (strErrMsg.length())
12196 mConsoleTaskData.mProgress->notifyComplete(iResult,
12197 COM_IIDOF(ISession),
12198 getComponentName(),
12199 strErrMsg.c_str());
12200 else
12201 mConsoleTaskData.mProgress->notifyComplete(iResult);
12202 }
12203
12204 /* clear out the temporary saved state data */
12205 mConsoleTaskData.mLastState = MachineState_Null;
12206 mConsoleTaskData.mProgress.setNull();
12207
12208 LogFlowThisFuncLeave();
12209 return S_OK;
12210}
12211
12212
12213/**
12214 * Goes through the USB filters of the given machine to see if the given
12215 * device matches any filter or not.
12216 *
12217 * @note Locks the same as USBController::hasMatchingFilter() does.
12218 */
12219STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12220 BOOL *aMatched,
12221 ULONG *aMaskedIfs)
12222{
12223 LogFlowThisFunc(("\n"));
12224
12225 CheckComArgNotNull(aUSBDevice);
12226 CheckComArgOutPointerValid(aMatched);
12227
12228 AutoCaller autoCaller(this);
12229 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12230
12231#ifdef VBOX_WITH_USB
12232 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12233#else
12234 NOREF(aUSBDevice);
12235 NOREF(aMaskedIfs);
12236 *aMatched = FALSE;
12237#endif
12238
12239 return S_OK;
12240}
12241
12242/**
12243 * @note Locks the same as Host::captureUSBDevice() does.
12244 */
12245STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12246{
12247 LogFlowThisFunc(("\n"));
12248
12249 AutoCaller autoCaller(this);
12250 AssertComRCReturnRC(autoCaller.rc());
12251
12252#ifdef VBOX_WITH_USB
12253 /* if captureDeviceForVM() fails, it must have set extended error info */
12254 clearError();
12255 MultiResult rc = mParent->host()->checkUSBProxyService();
12256 if (FAILED(rc)) return rc;
12257
12258 USBProxyService *service = mParent->host()->usbProxyService();
12259 AssertReturn(service, E_FAIL);
12260 return service->captureDeviceForVM(this, Guid(aId).ref());
12261#else
12262 NOREF(aId);
12263 return E_NOTIMPL;
12264#endif
12265}
12266
12267/**
12268 * @note Locks the same as Host::detachUSBDevice() does.
12269 */
12270STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12271{
12272 LogFlowThisFunc(("\n"));
12273
12274 AutoCaller autoCaller(this);
12275 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12276
12277#ifdef VBOX_WITH_USB
12278 USBProxyService *service = mParent->host()->usbProxyService();
12279 AssertReturn(service, E_FAIL);
12280 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12281#else
12282 NOREF(aId);
12283 NOREF(aDone);
12284 return E_NOTIMPL;
12285#endif
12286}
12287
12288/**
12289 * Inserts all machine filters to the USB proxy service and then calls
12290 * Host::autoCaptureUSBDevices().
12291 *
12292 * Called by Console from the VM process upon VM startup.
12293 *
12294 * @note Locks what called methods lock.
12295 */
12296STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12297{
12298 LogFlowThisFunc(("\n"));
12299
12300 AutoCaller autoCaller(this);
12301 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12302
12303#ifdef VBOX_WITH_USB
12304 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12305 AssertComRC(rc);
12306 NOREF(rc);
12307
12308 USBProxyService *service = mParent->host()->usbProxyService();
12309 AssertReturn(service, E_FAIL);
12310 return service->autoCaptureDevicesForVM(this);
12311#else
12312 return S_OK;
12313#endif
12314}
12315
12316/**
12317 * Removes all machine filters from the USB proxy service and then calls
12318 * Host::detachAllUSBDevices().
12319 *
12320 * Called by Console from the VM process upon normal VM termination or by
12321 * SessionMachine::uninit() upon abnormal VM termination (from under the
12322 * Machine/SessionMachine lock).
12323 *
12324 * @note Locks what called methods lock.
12325 */
12326STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12327{
12328 LogFlowThisFunc(("\n"));
12329
12330 AutoCaller autoCaller(this);
12331 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12332
12333#ifdef VBOX_WITH_USB
12334 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12335 AssertComRC(rc);
12336 NOREF(rc);
12337
12338 USBProxyService *service = mParent->host()->usbProxyService();
12339 AssertReturn(service, E_FAIL);
12340 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12341#else
12342 NOREF(aDone);
12343 return S_OK;
12344#endif
12345}
12346
12347/**
12348 * @note Locks this object for writing.
12349 */
12350STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12351 IProgress **aProgress)
12352{
12353 LogFlowThisFuncEnter();
12354
12355 AssertReturn(aSession, E_INVALIDARG);
12356 AssertReturn(aProgress, E_INVALIDARG);
12357
12358 AutoCaller autoCaller(this);
12359
12360 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12361 /*
12362 * We don't assert below because it might happen that a non-direct session
12363 * informs us it is closed right after we've been uninitialized -- it's ok.
12364 */
12365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12366
12367 /* get IInternalSessionControl interface */
12368 ComPtr<IInternalSessionControl> control(aSession);
12369
12370 ComAssertRet(!control.isNull(), E_INVALIDARG);
12371
12372 /* Creating a Progress object requires the VirtualBox lock, and
12373 * thus locking it here is required by the lock order rules. */
12374 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12375
12376 if (control == mData->mSession.mDirectControl)
12377 {
12378 ComAssertRet(aProgress, E_POINTER);
12379
12380 /* The direct session is being normally closed by the client process
12381 * ----------------------------------------------------------------- */
12382
12383 /* go to the closing state (essential for all open*Session() calls and
12384 * for #checkForDeath()) */
12385 Assert(mData->mSession.mState == SessionState_Locked);
12386 mData->mSession.mState = SessionState_Unlocking;
12387
12388 /* set direct control to NULL to release the remote instance */
12389 mData->mSession.mDirectControl.setNull();
12390 LogFlowThisFunc(("Direct control is set to NULL\n"));
12391
12392 if (mData->mSession.mProgress)
12393 {
12394 /* finalize the progress, someone might wait if a frontend
12395 * closes the session before powering on the VM. */
12396 mData->mSession.mProgress->notifyComplete(E_FAIL,
12397 COM_IIDOF(ISession),
12398 getComponentName(),
12399 tr("The VM session was closed before any attempt to power it on"));
12400 mData->mSession.mProgress.setNull();
12401 }
12402
12403 /* Create the progress object the client will use to wait until
12404 * #checkForDeath() is called to uninitialize this session object after
12405 * it releases the IPC semaphore.
12406 * Note! Because we're "reusing" mProgress here, this must be a proxy
12407 * object just like for LaunchVMProcess. */
12408 Assert(mData->mSession.mProgress.isNull());
12409 ComObjPtr<ProgressProxy> progress;
12410 progress.createObject();
12411 ComPtr<IUnknown> pPeer(mPeer);
12412 progress->init(mParent, pPeer,
12413 Bstr(tr("Closing session")).raw(),
12414 FALSE /* aCancelable */);
12415 progress.queryInterfaceTo(aProgress);
12416 mData->mSession.mProgress = progress;
12417 }
12418 else
12419 {
12420 /* the remote session is being normally closed */
12421 Data::Session::RemoteControlList::iterator it =
12422 mData->mSession.mRemoteControls.begin();
12423 while (it != mData->mSession.mRemoteControls.end())
12424 {
12425 if (control == *it)
12426 break;
12427 ++it;
12428 }
12429 BOOL found = it != mData->mSession.mRemoteControls.end();
12430 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12431 E_INVALIDARG);
12432 // This MUST be erase(it), not remove(*it) as the latter triggers a
12433 // very nasty use after free due to the place where the value "lives".
12434 mData->mSession.mRemoteControls.erase(it);
12435 }
12436
12437 /* signal the client watcher thread, because the client is going away */
12438 mParent->updateClientWatcher();
12439
12440 LogFlowThisFuncLeave();
12441 return S_OK;
12442}
12443
12444/**
12445 * @note Locks this object for writing.
12446 */
12447STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12448{
12449 LogFlowThisFuncEnter();
12450
12451 CheckComArgOutPointerValid(aProgress);
12452 CheckComArgOutPointerValid(aStateFilePath);
12453
12454 AutoCaller autoCaller(this);
12455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12456
12457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12458
12459 AssertReturn( mData->mMachineState == MachineState_Paused
12460 && mConsoleTaskData.mLastState == MachineState_Null
12461 && mConsoleTaskData.strStateFilePath.isEmpty(),
12462 E_FAIL);
12463
12464 /* create a progress object to track operation completion */
12465 ComObjPtr<Progress> pProgress;
12466 pProgress.createObject();
12467 pProgress->init(getVirtualBox(),
12468 static_cast<IMachine *>(this) /* aInitiator */,
12469 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12470 FALSE /* aCancelable */);
12471
12472 Utf8Str strStateFilePath;
12473 /* stateFilePath is null when the machine is not running */
12474 if (mData->mMachineState == MachineState_Paused)
12475 composeSavedStateFilename(strStateFilePath);
12476
12477 /* fill in the console task data */
12478 mConsoleTaskData.mLastState = mData->mMachineState;
12479 mConsoleTaskData.strStateFilePath = strStateFilePath;
12480 mConsoleTaskData.mProgress = pProgress;
12481
12482 /* set the state to Saving (this is expected by Console::SaveState()) */
12483 setMachineState(MachineState_Saving);
12484
12485 strStateFilePath.cloneTo(aStateFilePath);
12486 pProgress.queryInterfaceTo(aProgress);
12487
12488 return S_OK;
12489}
12490
12491/**
12492 * @note Locks mParent + this object for writing.
12493 */
12494STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12495{
12496 LogFlowThisFunc(("\n"));
12497
12498 AutoCaller autoCaller(this);
12499 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12500
12501 /* endSavingState() need mParent lock */
12502 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12503
12504 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12505 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12506 && mConsoleTaskData.mLastState != MachineState_Null
12507 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12508 E_FAIL);
12509
12510 /*
12511 * On failure, set the state to the state we had when BeginSavingState()
12512 * was called (this is expected by Console::SaveState() and the associated
12513 * task). On success the VM process already changed the state to
12514 * MachineState_Saved, so no need to do anything.
12515 */
12516 if (FAILED(iResult))
12517 setMachineState(mConsoleTaskData.mLastState);
12518
12519 return endSavingState(iResult, aErrMsg);
12520}
12521
12522/**
12523 * @note Locks this object for writing.
12524 */
12525STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12526{
12527 LogFlowThisFunc(("\n"));
12528
12529 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12530
12531 AutoCaller autoCaller(this);
12532 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12533
12534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12535
12536 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12537 || mData->mMachineState == MachineState_Teleported
12538 || mData->mMachineState == MachineState_Aborted
12539 , E_FAIL); /** @todo setError. */
12540
12541 Utf8Str stateFilePathFull = aSavedStateFile;
12542 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12543 if (RT_FAILURE(vrc))
12544 return setError(VBOX_E_FILE_ERROR,
12545 tr("Invalid saved state file path '%ls' (%Rrc)"),
12546 aSavedStateFile,
12547 vrc);
12548
12549 mSSData->strStateFilePath = stateFilePathFull;
12550
12551 /* The below setMachineState() will detect the state transition and will
12552 * update the settings file */
12553
12554 return setMachineState(MachineState_Saved);
12555}
12556
12557STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12558 ComSafeArrayOut(BSTR, aValues),
12559 ComSafeArrayOut(LONG64, aTimestamps),
12560 ComSafeArrayOut(BSTR, aFlags))
12561{
12562 LogFlowThisFunc(("\n"));
12563
12564#ifdef VBOX_WITH_GUEST_PROPS
12565 using namespace guestProp;
12566
12567 AutoCaller autoCaller(this);
12568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12569
12570 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12571
12572 CheckComArgOutSafeArrayPointerValid(aNames);
12573 CheckComArgOutSafeArrayPointerValid(aValues);
12574 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12575 CheckComArgOutSafeArrayPointerValid(aFlags);
12576
12577 size_t cEntries = mHWData->mGuestProperties.size();
12578 com::SafeArray<BSTR> names(cEntries);
12579 com::SafeArray<BSTR> values(cEntries);
12580 com::SafeArray<LONG64> timestamps(cEntries);
12581 com::SafeArray<BSTR> flags(cEntries);
12582 unsigned i = 0;
12583 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12584 it != mHWData->mGuestProperties.end();
12585 ++it)
12586 {
12587 char szFlags[MAX_FLAGS_LEN + 1];
12588 it->strName.cloneTo(&names[i]);
12589 it->strValue.cloneTo(&values[i]);
12590 timestamps[i] = it->mTimestamp;
12591 /* If it is NULL, keep it NULL. */
12592 if (it->mFlags)
12593 {
12594 writeFlags(it->mFlags, szFlags);
12595 Bstr(szFlags).cloneTo(&flags[i]);
12596 }
12597 else
12598 flags[i] = NULL;
12599 ++i;
12600 }
12601 names.detachTo(ComSafeArrayOutArg(aNames));
12602 values.detachTo(ComSafeArrayOutArg(aValues));
12603 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12604 flags.detachTo(ComSafeArrayOutArg(aFlags));
12605 return S_OK;
12606#else
12607 ReturnComNotImplemented();
12608#endif
12609}
12610
12611STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12612 IN_BSTR aValue,
12613 LONG64 aTimestamp,
12614 IN_BSTR aFlags)
12615{
12616 LogFlowThisFunc(("\n"));
12617
12618#ifdef VBOX_WITH_GUEST_PROPS
12619 using namespace guestProp;
12620
12621 CheckComArgStrNotEmptyOrNull(aName);
12622 CheckComArgNotNull(aValue);
12623 CheckComArgNotNull(aFlags);
12624
12625 try
12626 {
12627 /*
12628 * Convert input up front.
12629 */
12630 Utf8Str utf8Name(aName);
12631 uint32_t fFlags = NILFLAG;
12632 if (aFlags)
12633 {
12634 Utf8Str utf8Flags(aFlags);
12635 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12636 AssertRCReturn(vrc, E_INVALIDARG);
12637 }
12638
12639 /*
12640 * Now grab the object lock, validate the state and do the update.
12641 */
12642 AutoCaller autoCaller(this);
12643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12644
12645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12646
12647 switch (mData->mMachineState)
12648 {
12649 case MachineState_Paused:
12650 case MachineState_Running:
12651 case MachineState_Teleporting:
12652 case MachineState_TeleportingPausedVM:
12653 case MachineState_LiveSnapshotting:
12654 case MachineState_DeletingSnapshotOnline:
12655 case MachineState_DeletingSnapshotPaused:
12656 case MachineState_Saving:
12657 break;
12658
12659 default:
12660#ifndef DEBUG_sunlover
12661 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12662 VBOX_E_INVALID_VM_STATE);
12663#else
12664 return VBOX_E_INVALID_VM_STATE;
12665#endif
12666 }
12667
12668 setModified(IsModified_MachineData);
12669 mHWData.backup();
12670
12671 /** @todo r=bird: The careful memory handling doesn't work out here because
12672 * the catch block won't undo any damage we've done. So, if push_back throws
12673 * bad_alloc then you've lost the value.
12674 *
12675 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12676 * since values that changes actually bubbles to the end of the list. Using
12677 * something that has an efficient lookup and can tolerate a bit of updates
12678 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12679 * combination of RTStrCache (for sharing names and getting uniqueness into
12680 * the bargain) and hash/tree is another. */
12681 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12682 iter != mHWData->mGuestProperties.end();
12683 ++iter)
12684 if (utf8Name == iter->strName)
12685 {
12686 mHWData->mGuestProperties.erase(iter);
12687 mData->mGuestPropertiesModified = TRUE;
12688 break;
12689 }
12690 if (aValue != NULL)
12691 {
12692 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12693 mHWData->mGuestProperties.push_back(property);
12694 mData->mGuestPropertiesModified = TRUE;
12695 }
12696
12697 /*
12698 * Send a callback notification if appropriate
12699 */
12700 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12701 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12702 RTSTR_MAX,
12703 utf8Name.c_str(),
12704 RTSTR_MAX, NULL)
12705 )
12706 {
12707 alock.release();
12708
12709 mParent->onGuestPropertyChange(mData->mUuid,
12710 aName,
12711 aValue,
12712 aFlags);
12713 }
12714 }
12715 catch (...)
12716 {
12717 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12718 }
12719 return S_OK;
12720#else
12721 ReturnComNotImplemented();
12722#endif
12723}
12724
12725STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12726 IMediumAttachment **aNewAttachment)
12727{
12728 CheckComArgNotNull(aAttachment);
12729 CheckComArgOutPointerValid(aNewAttachment);
12730
12731 AutoCaller autoCaller(this);
12732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12733
12734 // request the host lock first, since might be calling Host methods for getting host drives;
12735 // next, protect the media tree all the while we're in here, as well as our member variables
12736 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12737 this->lockHandle(),
12738 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12739
12740 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12741
12742 Bstr ctrlName;
12743 LONG lPort;
12744 LONG lDevice;
12745 bool fTempEject;
12746 {
12747 AutoCaller autoAttachCaller(this);
12748 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12749
12750 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12751
12752 /* Need to query the details first, as the IMediumAttachment reference
12753 * might be to the original settings, which we are going to change. */
12754 ctrlName = pAttach->getControllerName();
12755 lPort = pAttach->getPort();
12756 lDevice = pAttach->getDevice();
12757 fTempEject = pAttach->getTempEject();
12758 }
12759
12760 if (!fTempEject)
12761 {
12762 /* Remember previously mounted medium. The medium before taking the
12763 * backup is not necessarily the same thing. */
12764 ComObjPtr<Medium> oldmedium;
12765 oldmedium = pAttach->getMedium();
12766
12767 setModified(IsModified_Storage);
12768 mMediaData.backup();
12769
12770 // The backup operation makes the pAttach reference point to the
12771 // old settings. Re-get the correct reference.
12772 pAttach = findAttachment(mMediaData->mAttachments,
12773 ctrlName.raw(),
12774 lPort,
12775 lDevice);
12776
12777 {
12778 AutoCaller autoAttachCaller(this);
12779 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12780
12781 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12782 if (!oldmedium.isNull())
12783 oldmedium->removeBackReference(mData->mUuid);
12784
12785 pAttach->updateMedium(NULL);
12786 pAttach->updateEjected();
12787 }
12788
12789 setModified(IsModified_Storage);
12790 }
12791 else
12792 {
12793 {
12794 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12795 pAttach->updateEjected();
12796 }
12797 }
12798
12799 pAttach.queryInterfaceTo(aNewAttachment);
12800
12801 return S_OK;
12802}
12803
12804// public methods only for internal purposes
12805/////////////////////////////////////////////////////////////////////////////
12806
12807/**
12808 * Called from the client watcher thread to check for expected or unexpected
12809 * death of the client process that has a direct session to this machine.
12810 *
12811 * On Win32 and on OS/2, this method is called only when we've got the
12812 * mutex (i.e. the client has either died or terminated normally) so it always
12813 * returns @c true (the client is terminated, the session machine is
12814 * uninitialized).
12815 *
12816 * On other platforms, the method returns @c true if the client process has
12817 * terminated normally or abnormally and the session machine was uninitialized,
12818 * and @c false if the client process is still alive.
12819 *
12820 * @note Locks this object for writing.
12821 */
12822bool SessionMachine::checkForDeath()
12823{
12824 Uninit::Reason reason;
12825 bool terminated = false;
12826
12827 /* Enclose autoCaller with a block because calling uninit() from under it
12828 * will deadlock. */
12829 {
12830 AutoCaller autoCaller(this);
12831 if (!autoCaller.isOk())
12832 {
12833 /* return true if not ready, to cause the client watcher to exclude
12834 * the corresponding session from watching */
12835 LogFlowThisFunc(("Already uninitialized!\n"));
12836 return true;
12837 }
12838
12839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12840
12841 /* Determine the reason of death: if the session state is Closing here,
12842 * everything is fine. Otherwise it means that the client did not call
12843 * OnSessionEnd() before it released the IPC semaphore. This may happen
12844 * either because the client process has abnormally terminated, or
12845 * because it simply forgot to call ISession::Close() before exiting. We
12846 * threat the latter also as an abnormal termination (see
12847 * Session::uninit() for details). */
12848 reason = mData->mSession.mState == SessionState_Unlocking ?
12849 Uninit::Normal :
12850 Uninit::Abnormal;
12851
12852#if defined(RT_OS_WINDOWS)
12853
12854 AssertMsg(mIPCSem, ("semaphore must be created"));
12855
12856 /* release the IPC mutex */
12857 ::ReleaseMutex(mIPCSem);
12858
12859 terminated = true;
12860
12861#elif defined(RT_OS_OS2)
12862
12863 AssertMsg(mIPCSem, ("semaphore must be created"));
12864
12865 /* release the IPC mutex */
12866 ::DosReleaseMutexSem(mIPCSem);
12867
12868 terminated = true;
12869
12870#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12871
12872 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12873
12874 int val = ::semctl(mIPCSem, 0, GETVAL);
12875 if (val > 0)
12876 {
12877 /* the semaphore is signaled, meaning the session is terminated */
12878 terminated = true;
12879 }
12880
12881#else
12882# error "Port me!"
12883#endif
12884
12885 } /* AutoCaller block */
12886
12887 if (terminated)
12888 uninit(reason);
12889
12890 return terminated;
12891}
12892
12893/**
12894 * @note Locks this object for reading.
12895 */
12896HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12897{
12898 LogFlowThisFunc(("\n"));
12899
12900 AutoCaller autoCaller(this);
12901 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12902
12903 ComPtr<IInternalSessionControl> directControl;
12904 {
12905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12906 directControl = mData->mSession.mDirectControl;
12907 }
12908
12909 /* ignore notifications sent after #OnSessionEnd() is called */
12910 if (!directControl)
12911 return S_OK;
12912
12913 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12914}
12915
12916/**
12917 * @note Locks this object for reading.
12918 */
12919HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12920 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12921{
12922 LogFlowThisFunc(("\n"));
12923
12924 AutoCaller autoCaller(this);
12925 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12926
12927 ComPtr<IInternalSessionControl> directControl;
12928 {
12929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12930 directControl = mData->mSession.mDirectControl;
12931 }
12932
12933 /* ignore notifications sent after #OnSessionEnd() is called */
12934 if (!directControl)
12935 return S_OK;
12936 /*
12937 * instead acting like callback we ask IVirtualBox deliver corresponding event
12938 */
12939
12940 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12941 return S_OK;
12942}
12943
12944/**
12945 * @note Locks this object for reading.
12946 */
12947HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12948{
12949 LogFlowThisFunc(("\n"));
12950
12951 AutoCaller autoCaller(this);
12952 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12953
12954 ComPtr<IInternalSessionControl> directControl;
12955 {
12956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12957 directControl = mData->mSession.mDirectControl;
12958 }
12959
12960 /* ignore notifications sent after #OnSessionEnd() is called */
12961 if (!directControl)
12962 return S_OK;
12963
12964 return directControl->OnSerialPortChange(serialPort);
12965}
12966
12967/**
12968 * @note Locks this object for reading.
12969 */
12970HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12971{
12972 LogFlowThisFunc(("\n"));
12973
12974 AutoCaller autoCaller(this);
12975 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12976
12977 ComPtr<IInternalSessionControl> directControl;
12978 {
12979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12980 directControl = mData->mSession.mDirectControl;
12981 }
12982
12983 /* ignore notifications sent after #OnSessionEnd() is called */
12984 if (!directControl)
12985 return S_OK;
12986
12987 return directControl->OnParallelPortChange(parallelPort);
12988}
12989
12990/**
12991 * @note Locks this object for reading.
12992 */
12993HRESULT SessionMachine::onStorageControllerChange()
12994{
12995 LogFlowThisFunc(("\n"));
12996
12997 AutoCaller autoCaller(this);
12998 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12999
13000 ComPtr<IInternalSessionControl> directControl;
13001 {
13002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13003 directControl = mData->mSession.mDirectControl;
13004 }
13005
13006 /* ignore notifications sent after #OnSessionEnd() is called */
13007 if (!directControl)
13008 return S_OK;
13009
13010 return directControl->OnStorageControllerChange();
13011}
13012
13013/**
13014 * @note Locks this object for reading.
13015 */
13016HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13017{
13018 LogFlowThisFunc(("\n"));
13019
13020 AutoCaller autoCaller(this);
13021 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13022
13023 ComPtr<IInternalSessionControl> directControl;
13024 {
13025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13026 directControl = mData->mSession.mDirectControl;
13027 }
13028
13029 /* ignore notifications sent after #OnSessionEnd() is called */
13030 if (!directControl)
13031 return S_OK;
13032
13033 return directControl->OnMediumChange(aAttachment, aForce);
13034}
13035
13036/**
13037 * @note Locks this object for reading.
13038 */
13039HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13040{
13041 LogFlowThisFunc(("\n"));
13042
13043 AutoCaller autoCaller(this);
13044 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13045
13046 ComPtr<IInternalSessionControl> directControl;
13047 {
13048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13049 directControl = mData->mSession.mDirectControl;
13050 }
13051
13052 /* ignore notifications sent after #OnSessionEnd() is called */
13053 if (!directControl)
13054 return S_OK;
13055
13056 return directControl->OnCPUChange(aCPU, aRemove);
13057}
13058
13059HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13060{
13061 LogFlowThisFunc(("\n"));
13062
13063 AutoCaller autoCaller(this);
13064 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13065
13066 ComPtr<IInternalSessionControl> directControl;
13067 {
13068 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13069 directControl = mData->mSession.mDirectControl;
13070 }
13071
13072 /* ignore notifications sent after #OnSessionEnd() is called */
13073 if (!directControl)
13074 return S_OK;
13075
13076 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13077}
13078
13079/**
13080 * @note Locks this object for reading.
13081 */
13082HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13083{
13084 LogFlowThisFunc(("\n"));
13085
13086 AutoCaller autoCaller(this);
13087 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13088
13089 ComPtr<IInternalSessionControl> directControl;
13090 {
13091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13092 directControl = mData->mSession.mDirectControl;
13093 }
13094
13095 /* ignore notifications sent after #OnSessionEnd() is called */
13096 if (!directControl)
13097 return S_OK;
13098
13099 return directControl->OnVRDEServerChange(aRestart);
13100}
13101
13102/**
13103 * @note Locks this object for reading.
13104 */
13105HRESULT SessionMachine::onUSBControllerChange()
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 return directControl->OnUSBControllerChange();
13123}
13124
13125/**
13126 * @note Locks this object for reading.
13127 */
13128HRESULT SessionMachine::onSharedFolderChange()
13129{
13130 LogFlowThisFunc(("\n"));
13131
13132 AutoCaller autoCaller(this);
13133 AssertComRCReturnRC(autoCaller.rc());
13134
13135 ComPtr<IInternalSessionControl> directControl;
13136 {
13137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13138 directControl = mData->mSession.mDirectControl;
13139 }
13140
13141 /* ignore notifications sent after #OnSessionEnd() is called */
13142 if (!directControl)
13143 return S_OK;
13144
13145 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13146}
13147
13148/**
13149 * @note Locks this object for reading.
13150 */
13151HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13152{
13153 LogFlowThisFunc(("\n"));
13154
13155 AutoCaller autoCaller(this);
13156 AssertComRCReturnRC(autoCaller.rc());
13157
13158 ComPtr<IInternalSessionControl> directControl;
13159 {
13160 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13161 directControl = mData->mSession.mDirectControl;
13162 }
13163
13164 /* ignore notifications sent after #OnSessionEnd() is called */
13165 if (!directControl)
13166 return S_OK;
13167
13168 return directControl->OnClipboardModeChange(aClipboardMode);
13169}
13170
13171/**
13172 * @note Locks this object for reading.
13173 */
13174HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13175{
13176 LogFlowThisFunc(("\n"));
13177
13178 AutoCaller autoCaller(this);
13179 AssertComRCReturnRC(autoCaller.rc());
13180
13181 ComPtr<IInternalSessionControl> directControl;
13182 {
13183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13184 directControl = mData->mSession.mDirectControl;
13185 }
13186
13187 /* ignore notifications sent after #OnSessionEnd() is called */
13188 if (!directControl)
13189 return S_OK;
13190
13191 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13192}
13193
13194/**
13195 * @note Locks this object for reading.
13196 */
13197HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13198{
13199 LogFlowThisFunc(("\n"));
13200
13201 AutoCaller autoCaller(this);
13202 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13203
13204 ComPtr<IInternalSessionControl> directControl;
13205 {
13206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13207 directControl = mData->mSession.mDirectControl;
13208 }
13209
13210 /* ignore notifications sent after #OnSessionEnd() is called */
13211 if (!directControl)
13212 return S_OK;
13213
13214 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13215}
13216
13217/**
13218 * @note Locks this object for reading.
13219 */
13220HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13221{
13222 LogFlowThisFunc(("\n"));
13223
13224 AutoCaller autoCaller(this);
13225 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13226
13227 ComPtr<IInternalSessionControl> directControl;
13228 {
13229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13230 directControl = mData->mSession.mDirectControl;
13231 }
13232
13233 /* ignore notifications sent after #OnSessionEnd() is called */
13234 if (!directControl)
13235 return S_OK;
13236
13237 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13238}
13239
13240/**
13241 * Returns @c true if this machine's USB controller reports it has a matching
13242 * filter for the given USB device and @c false otherwise.
13243 *
13244 * @note locks this object for reading.
13245 */
13246bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13247{
13248 AutoCaller autoCaller(this);
13249 /* silently return if not ready -- this method may be called after the
13250 * direct machine session has been called */
13251 if (!autoCaller.isOk())
13252 return false;
13253
13254#ifdef VBOX_WITH_USB
13255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13256
13257 switch (mData->mMachineState)
13258 {
13259 case MachineState_Starting:
13260 case MachineState_Restoring:
13261 case MachineState_TeleportingIn:
13262 case MachineState_Paused:
13263 case MachineState_Running:
13264 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13265 * elsewhere... */
13266 alock.release();
13267 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13268 default: break;
13269 }
13270#else
13271 NOREF(aDevice);
13272 NOREF(aMaskedIfs);
13273#endif
13274 return false;
13275}
13276
13277/**
13278 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13279 */
13280HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13281 IVirtualBoxErrorInfo *aError,
13282 ULONG aMaskedIfs)
13283{
13284 LogFlowThisFunc(("\n"));
13285
13286 AutoCaller autoCaller(this);
13287
13288 /* This notification may happen after the machine object has been
13289 * uninitialized (the session was closed), so don't assert. */
13290 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13291
13292 ComPtr<IInternalSessionControl> directControl;
13293 {
13294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13295 directControl = mData->mSession.mDirectControl;
13296 }
13297
13298 /* fail on notifications sent after #OnSessionEnd() is called, it is
13299 * expected by the caller */
13300 if (!directControl)
13301 return E_FAIL;
13302
13303 /* No locks should be held at this point. */
13304 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13305 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13306
13307 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13308}
13309
13310/**
13311 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13312 */
13313HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13314 IVirtualBoxErrorInfo *aError)
13315{
13316 LogFlowThisFunc(("\n"));
13317
13318 AutoCaller autoCaller(this);
13319
13320 /* This notification may happen after the machine object has been
13321 * uninitialized (the session was closed), so don't assert. */
13322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13323
13324 ComPtr<IInternalSessionControl> directControl;
13325 {
13326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13327 directControl = mData->mSession.mDirectControl;
13328 }
13329
13330 /* fail on notifications sent after #OnSessionEnd() is called, it is
13331 * expected by the caller */
13332 if (!directControl)
13333 return E_FAIL;
13334
13335 /* No locks should be held at this point. */
13336 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13337 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13338
13339 return directControl->OnUSBDeviceDetach(aId, aError);
13340}
13341
13342// protected methods
13343/////////////////////////////////////////////////////////////////////////////
13344
13345/**
13346 * Helper method to finalize saving the state.
13347 *
13348 * @note Must be called from under this object's lock.
13349 *
13350 * @param aRc S_OK if the snapshot has been taken successfully
13351 * @param aErrMsg human readable error message for failure
13352 *
13353 * @note Locks mParent + this objects for writing.
13354 */
13355HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13356{
13357 LogFlowThisFuncEnter();
13358
13359 AutoCaller autoCaller(this);
13360 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13361
13362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13363
13364 HRESULT rc = S_OK;
13365
13366 if (SUCCEEDED(aRc))
13367 {
13368 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13369
13370 /* save all VM settings */
13371 rc = saveSettings(NULL);
13372 // no need to check whether VirtualBox.xml needs saving also since
13373 // we can't have a name change pending at this point
13374 }
13375 else
13376 {
13377 // delete the saved state file (it might have been already created);
13378 // we need not check whether this is shared with a snapshot here because
13379 // we certainly created this saved state file here anew
13380 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13381 }
13382
13383 /* notify the progress object about operation completion */
13384 Assert(mConsoleTaskData.mProgress);
13385 if (SUCCEEDED(aRc))
13386 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13387 else
13388 {
13389 if (aErrMsg.length())
13390 mConsoleTaskData.mProgress->notifyComplete(aRc,
13391 COM_IIDOF(ISession),
13392 getComponentName(),
13393 aErrMsg.c_str());
13394 else
13395 mConsoleTaskData.mProgress->notifyComplete(aRc);
13396 }
13397
13398 /* clear out the temporary saved state data */
13399 mConsoleTaskData.mLastState = MachineState_Null;
13400 mConsoleTaskData.strStateFilePath.setNull();
13401 mConsoleTaskData.mProgress.setNull();
13402
13403 LogFlowThisFuncLeave();
13404 return rc;
13405}
13406
13407/**
13408 * Deletes the given file if it is no longer in use by either the current machine state
13409 * (if the machine is "saved") or any of the machine's snapshots.
13410 *
13411 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13412 * but is different for each SnapshotMachine. When calling this, the order of calling this
13413 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13414 * is therefore critical. I know, it's all rather messy.
13415 *
13416 * @param strStateFile
13417 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13418 */
13419void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13420 Snapshot *pSnapshotToIgnore)
13421{
13422 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13423 if ( (strStateFile.isNotEmpty())
13424 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13425 )
13426 // ... and it must also not be shared with other snapshots
13427 if ( !mData->mFirstSnapshot
13428 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13429 // this checks the SnapshotMachine's state file paths
13430 )
13431 RTFileDelete(strStateFile.c_str());
13432}
13433
13434/**
13435 * Locks the attached media.
13436 *
13437 * All attached hard disks are locked for writing and DVD/floppy are locked for
13438 * reading. Parents of attached hard disks (if any) are locked for reading.
13439 *
13440 * This method also performs accessibility check of all media it locks: if some
13441 * media is inaccessible, the method will return a failure and a bunch of
13442 * extended error info objects per each inaccessible medium.
13443 *
13444 * Note that this method is atomic: if it returns a success, all media are
13445 * locked as described above; on failure no media is locked at all (all
13446 * succeeded individual locks will be undone).
13447 *
13448 * This method is intended to be called when the machine is in Starting or
13449 * Restoring state and asserts otherwise.
13450 *
13451 * The locks made by this method must be undone by calling #unlockMedia() when
13452 * no more needed.
13453 */
13454HRESULT SessionMachine::lockMedia()
13455{
13456 AutoCaller autoCaller(this);
13457 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13458
13459 AutoMultiWriteLock2 alock(this->lockHandle(),
13460 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13461
13462 AssertReturn( mData->mMachineState == MachineState_Starting
13463 || mData->mMachineState == MachineState_Restoring
13464 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13465 /* bail out if trying to lock things with already set up locking */
13466 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13467
13468 clearError();
13469 MultiResult mrc(S_OK);
13470
13471 /* Collect locking information for all medium objects attached to the VM. */
13472 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13473 it != mMediaData->mAttachments.end();
13474 ++it)
13475 {
13476 MediumAttachment* pAtt = *it;
13477 DeviceType_T devType = pAtt->getType();
13478 Medium *pMedium = pAtt->getMedium();
13479
13480 MediumLockList *pMediumLockList(new MediumLockList());
13481 // There can be attachments without a medium (floppy/dvd), and thus
13482 // it's impossible to create a medium lock list. It still makes sense
13483 // to have the empty medium lock list in the map in case a medium is
13484 // attached later.
13485 if (pMedium != NULL)
13486 {
13487 MediumType_T mediumType = pMedium->getType();
13488 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13489 || mediumType == MediumType_Shareable;
13490 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13491
13492 alock.release();
13493 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13494 !fIsReadOnlyLock /* fMediumLockWrite */,
13495 NULL,
13496 *pMediumLockList);
13497 alock.acquire();
13498 if (FAILED(mrc))
13499 {
13500 delete pMediumLockList;
13501 mData->mSession.mLockedMedia.Clear();
13502 break;
13503 }
13504 }
13505
13506 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13507 if (FAILED(rc))
13508 {
13509 mData->mSession.mLockedMedia.Clear();
13510 mrc = setError(rc,
13511 tr("Collecting locking information for all attached media failed"));
13512 break;
13513 }
13514 }
13515
13516 if (SUCCEEDED(mrc))
13517 {
13518 /* Now lock all media. If this fails, nothing is locked. */
13519 alock.release();
13520 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13521 alock.acquire();
13522 if (FAILED(rc))
13523 {
13524 mrc = setError(rc,
13525 tr("Locking of attached media failed"));
13526 }
13527 }
13528
13529 return mrc;
13530}
13531
13532/**
13533 * Undoes the locks made by by #lockMedia().
13534 */
13535void SessionMachine::unlockMedia()
13536{
13537 AutoCaller autoCaller(this);
13538 AssertComRCReturnVoid(autoCaller.rc());
13539
13540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13541
13542 /* we may be holding important error info on the current thread;
13543 * preserve it */
13544 ErrorInfoKeeper eik;
13545
13546 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13547 AssertComRC(rc);
13548}
13549
13550/**
13551 * Helper to change the machine state (reimplementation).
13552 *
13553 * @note Locks this object for writing.
13554 * @note This method must not call saveSettings or SaveSettings, otherwise
13555 * it can cause crashes in random places due to unexpectedly committing
13556 * the current settings. The caller is responsible for that. The call
13557 * to saveStateSettings is fine, because this method does not commit.
13558 */
13559HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13560{
13561 LogFlowThisFuncEnter();
13562 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13563
13564 AutoCaller autoCaller(this);
13565 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13566
13567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13568
13569 MachineState_T oldMachineState = mData->mMachineState;
13570
13571 AssertMsgReturn(oldMachineState != aMachineState,
13572 ("oldMachineState=%s, aMachineState=%s\n",
13573 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13574 E_FAIL);
13575
13576 HRESULT rc = S_OK;
13577
13578 int stsFlags = 0;
13579 bool deleteSavedState = false;
13580
13581 /* detect some state transitions */
13582
13583 if ( ( oldMachineState == MachineState_Saved
13584 && aMachineState == MachineState_Restoring)
13585 || ( ( oldMachineState == MachineState_PoweredOff
13586 || oldMachineState == MachineState_Teleported
13587 || oldMachineState == MachineState_Aborted
13588 )
13589 && ( aMachineState == MachineState_TeleportingIn
13590 || aMachineState == MachineState_Starting
13591 )
13592 )
13593 )
13594 {
13595 /* The EMT thread is about to start */
13596
13597 /* Nothing to do here for now... */
13598
13599 /// @todo NEWMEDIA don't let mDVDDrive and other children
13600 /// change anything when in the Starting/Restoring state
13601 }
13602 else if ( ( oldMachineState == MachineState_Running
13603 || oldMachineState == MachineState_Paused
13604 || oldMachineState == MachineState_Teleporting
13605 || oldMachineState == MachineState_LiveSnapshotting
13606 || oldMachineState == MachineState_Stuck
13607 || oldMachineState == MachineState_Starting
13608 || oldMachineState == MachineState_Stopping
13609 || oldMachineState == MachineState_Saving
13610 || oldMachineState == MachineState_Restoring
13611 || oldMachineState == MachineState_TeleportingPausedVM
13612 || oldMachineState == MachineState_TeleportingIn
13613 )
13614 && ( aMachineState == MachineState_PoweredOff
13615 || aMachineState == MachineState_Saved
13616 || aMachineState == MachineState_Teleported
13617 || aMachineState == MachineState_Aborted
13618 )
13619 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13620 * snapshot */
13621 && ( mConsoleTaskData.mSnapshot.isNull()
13622 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13623 )
13624 )
13625 {
13626 /* The EMT thread has just stopped, unlock attached media. Note that as
13627 * opposed to locking that is done from Console, we do unlocking here
13628 * because the VM process may have aborted before having a chance to
13629 * properly unlock all media it locked. */
13630
13631 unlockMedia();
13632 }
13633
13634 if (oldMachineState == MachineState_Restoring)
13635 {
13636 if (aMachineState != MachineState_Saved)
13637 {
13638 /*
13639 * delete the saved state file once the machine has finished
13640 * restoring from it (note that Console sets the state from
13641 * Restoring to Saved if the VM couldn't restore successfully,
13642 * to give the user an ability to fix an error and retry --
13643 * we keep the saved state file in this case)
13644 */
13645 deleteSavedState = true;
13646 }
13647 }
13648 else if ( oldMachineState == MachineState_Saved
13649 && ( aMachineState == MachineState_PoweredOff
13650 || aMachineState == MachineState_Aborted
13651 || aMachineState == MachineState_Teleported
13652 )
13653 )
13654 {
13655 /*
13656 * delete the saved state after Console::ForgetSavedState() is called
13657 * or if the VM process (owning a direct VM session) crashed while the
13658 * VM was Saved
13659 */
13660
13661 /// @todo (dmik)
13662 // Not sure that deleting the saved state file just because of the
13663 // client death before it attempted to restore the VM is a good
13664 // thing. But when it crashes we need to go to the Aborted state
13665 // which cannot have the saved state file associated... The only
13666 // way to fix this is to make the Aborted condition not a VM state
13667 // but a bool flag: i.e., when a crash occurs, set it to true and
13668 // change the state to PoweredOff or Saved depending on the
13669 // saved state presence.
13670
13671 deleteSavedState = true;
13672 mData->mCurrentStateModified = TRUE;
13673 stsFlags |= SaveSTS_CurStateModified;
13674 }
13675
13676 if ( aMachineState == MachineState_Starting
13677 || aMachineState == MachineState_Restoring
13678 || aMachineState == MachineState_TeleportingIn
13679 )
13680 {
13681 /* set the current state modified flag to indicate that the current
13682 * state is no more identical to the state in the
13683 * current snapshot */
13684 if (!mData->mCurrentSnapshot.isNull())
13685 {
13686 mData->mCurrentStateModified = TRUE;
13687 stsFlags |= SaveSTS_CurStateModified;
13688 }
13689 }
13690
13691 if (deleteSavedState)
13692 {
13693 if (mRemoveSavedState)
13694 {
13695 Assert(!mSSData->strStateFilePath.isEmpty());
13696
13697 // it is safe to delete the saved state file if ...
13698 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13699 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13700 // ... none of the snapshots share the saved state file
13701 )
13702 RTFileDelete(mSSData->strStateFilePath.c_str());
13703 }
13704
13705 mSSData->strStateFilePath.setNull();
13706 stsFlags |= SaveSTS_StateFilePath;
13707 }
13708
13709 /* redirect to the underlying peer machine */
13710 mPeer->setMachineState(aMachineState);
13711
13712 if ( aMachineState == MachineState_PoweredOff
13713 || aMachineState == MachineState_Teleported
13714 || aMachineState == MachineState_Aborted
13715 || aMachineState == MachineState_Saved)
13716 {
13717 /* the machine has stopped execution
13718 * (or the saved state file was adopted) */
13719 stsFlags |= SaveSTS_StateTimeStamp;
13720 }
13721
13722 if ( ( oldMachineState == MachineState_PoweredOff
13723 || oldMachineState == MachineState_Aborted
13724 || oldMachineState == MachineState_Teleported
13725 )
13726 && aMachineState == MachineState_Saved)
13727 {
13728 /* the saved state file was adopted */
13729 Assert(!mSSData->strStateFilePath.isEmpty());
13730 stsFlags |= SaveSTS_StateFilePath;
13731 }
13732
13733#ifdef VBOX_WITH_GUEST_PROPS
13734 if ( aMachineState == MachineState_PoweredOff
13735 || aMachineState == MachineState_Aborted
13736 || aMachineState == MachineState_Teleported)
13737 {
13738 /* Make sure any transient guest properties get removed from the
13739 * property store on shutdown. */
13740
13741 HWData::GuestPropertyList::iterator it;
13742 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13743 if (!fNeedsSaving)
13744 for (it = mHWData->mGuestProperties.begin();
13745 it != mHWData->mGuestProperties.end(); ++it)
13746 if ( (it->mFlags & guestProp::TRANSIENT)
13747 || (it->mFlags & guestProp::TRANSRESET))
13748 {
13749 fNeedsSaving = true;
13750 break;
13751 }
13752 if (fNeedsSaving)
13753 {
13754 mData->mCurrentStateModified = TRUE;
13755 stsFlags |= SaveSTS_CurStateModified;
13756 }
13757 }
13758#endif
13759
13760 rc = saveStateSettings(stsFlags);
13761
13762 if ( ( oldMachineState != MachineState_PoweredOff
13763 && oldMachineState != MachineState_Aborted
13764 && oldMachineState != MachineState_Teleported
13765 )
13766 && ( aMachineState == MachineState_PoweredOff
13767 || aMachineState == MachineState_Aborted
13768 || aMachineState == MachineState_Teleported
13769 )
13770 )
13771 {
13772 /* we've been shut down for any reason */
13773 /* no special action so far */
13774 }
13775
13776 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13777 LogFlowThisFuncLeave();
13778 return rc;
13779}
13780
13781/**
13782 * Sends the current machine state value to the VM process.
13783 *
13784 * @note Locks this object for reading, then calls a client process.
13785 */
13786HRESULT SessionMachine::updateMachineStateOnClient()
13787{
13788 AutoCaller autoCaller(this);
13789 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13790
13791 ComPtr<IInternalSessionControl> directControl;
13792 {
13793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13794 AssertReturn(!!mData, E_FAIL);
13795 directControl = mData->mSession.mDirectControl;
13796
13797 /* directControl may be already set to NULL here in #OnSessionEnd()
13798 * called too early by the direct session process while there is still
13799 * some operation (like deleting the snapshot) in progress. The client
13800 * process in this case is waiting inside Session::close() for the
13801 * "end session" process object to complete, while #uninit() called by
13802 * #checkForDeath() on the Watcher thread is waiting for the pending
13803 * operation to complete. For now, we accept this inconsistent behavior
13804 * and simply do nothing here. */
13805
13806 if (mData->mSession.mState == SessionState_Unlocking)
13807 return S_OK;
13808
13809 AssertReturn(!directControl.isNull(), E_FAIL);
13810 }
13811
13812 return directControl->UpdateMachineState(mData->mMachineState);
13813}
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