VirtualBox

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

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

Main/Machine: fix lock order violation on machine unregistration

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 462.6 KB
Line 
1/* $Id: MachineImpl.cpp 43185 2012-09-05 09:00:05Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = true;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine()
247 : mCollectorGuest(NULL),
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->applyDefaults(aOsType);
352 }
353
354 /* At this point the changing of the current state modification
355 * flag is allowed. */
356 allowStateModification();
357
358 /* commit all changes made during the initialization */
359 commit();
360 }
361
362 /* Confirm a successful initialization when it's the case */
363 if (SUCCEEDED(rc))
364 {
365 if (mData->mAccessible)
366 autoInitSpan.setSucceeded();
367 else
368 autoInitSpan.setLimited();
369 }
370
371 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
372 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
373 mData->mRegistered,
374 mData->mAccessible,
375 rc));
376
377 LogFlowThisFuncLeave();
378
379 return rc;
380}
381
382/**
383 * Initializes a new instance with data from machine XML (formerly Init_Registered).
384 * Gets called in two modes:
385 *
386 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
387 * UUID is specified and we mark the machine as "registered";
388 *
389 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
390 * and the machine remains unregistered until RegisterMachine() is called.
391 *
392 * @param aParent Associated parent object
393 * @param aConfigFile Local file system path to the VM settings file (can
394 * be relative to the VirtualBox config directory).
395 * @param aId UUID of the machine or NULL (see above).
396 *
397 * @return Success indicator. if not S_OK, the machine object is invalid
398 */
399HRESULT Machine::initFromSettings(VirtualBox *aParent,
400 const Utf8Str &strConfigFile,
401 const Guid *aId)
402{
403 LogFlowThisFuncEnter();
404 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
405
406 /* Enclose the state transition NotReady->InInit->Ready */
407 AutoInitSpan autoInitSpan(this);
408 AssertReturn(autoInitSpan.isOk(), E_FAIL);
409
410 HRESULT rc = initImpl(aParent, strConfigFile);
411 if (FAILED(rc)) return rc;
412
413 if (aId)
414 {
415 // loading a registered VM:
416 unconst(mData->mUuid) = *aId;
417 mData->mRegistered = TRUE;
418 // now load the settings from XML:
419 rc = registeredInit();
420 // this calls initDataAndChildObjects() and loadSettings()
421 }
422 else
423 {
424 // opening an unregistered VM (VirtualBox::OpenMachine()):
425 rc = initDataAndChildObjects();
426
427 if (SUCCEEDED(rc))
428 {
429 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
430 mData->mAccessible = TRUE;
431
432 try
433 {
434 // load and parse machine XML; this will throw on XML or logic errors
435 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
436
437 // reject VM UUID duplicates, they can happen if someone
438 // tries to register an already known VM config again
439 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
440 true /* fPermitInaccessible */,
441 false /* aDoSetError */,
442 NULL) != VBOX_E_OBJECT_NOT_FOUND)
443 {
444 throw setError(E_FAIL,
445 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
446 mData->m_strConfigFile.c_str());
447 }
448
449 // use UUID from machine config
450 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
451
452 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
453 NULL /* puuidRegistry */);
454 if (FAILED(rc)) throw rc;
455
456 /* At this point the changing of the current state modification
457 * flag is allowed. */
458 allowStateModification();
459
460 commit();
461 }
462 catch (HRESULT err)
463 {
464 /* we assume that error info is set by the thrower */
465 rc = err;
466 }
467 catch (...)
468 {
469 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
470 }
471 }
472 }
473
474 /* Confirm a successful initialization when it's the case */
475 if (SUCCEEDED(rc))
476 {
477 if (mData->mAccessible)
478 autoInitSpan.setSucceeded();
479 else
480 {
481 autoInitSpan.setLimited();
482
483 // uninit media from this machine's media registry, or else
484 // reloading the settings will fail
485 mParent->unregisterMachineMedia(getId());
486 }
487 }
488
489 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
490 "rc=%08X\n",
491 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
492 mData->mRegistered, mData->mAccessible, rc));
493
494 LogFlowThisFuncLeave();
495
496 return rc;
497}
498
499/**
500 * Initializes a new instance from a machine config that is already in memory
501 * (import OVF case). Since we are importing, the UUID in the machine
502 * config is ignored and we always generate a fresh one.
503 *
504 * @param strName Name for the new machine; this overrides what is specified in config and is used
505 * for the settings file as well.
506 * @param config Machine configuration loaded and parsed from XML.
507 *
508 * @return Success indicator. if not S_OK, the machine object is invalid
509 */
510HRESULT Machine::init(VirtualBox *aParent,
511 const Utf8Str &strName,
512 const settings::MachineConfigFile &config)
513{
514 LogFlowThisFuncEnter();
515
516 /* Enclose the state transition NotReady->InInit->Ready */
517 AutoInitSpan autoInitSpan(this);
518 AssertReturn(autoInitSpan.isOk(), E_FAIL);
519
520 Utf8Str strConfigFile;
521 aParent->getDefaultMachineFolder(strConfigFile);
522 strConfigFile.append(RTPATH_DELIMITER);
523 strConfigFile.append(strName);
524 strConfigFile.append(RTPATH_DELIMITER);
525 strConfigFile.append(strName);
526 strConfigFile.append(".vbox");
527
528 HRESULT rc = initImpl(aParent, strConfigFile);
529 if (FAILED(rc)) return rc;
530
531 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
532 if (FAILED(rc)) return rc;
533
534 rc = initDataAndChildObjects();
535
536 if (SUCCEEDED(rc))
537 {
538 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
539 mData->mAccessible = TRUE;
540
541 // create empty machine config for instance data
542 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
543
544 // generate fresh UUID, ignore machine config
545 unconst(mData->mUuid).create();
546
547 rc = loadMachineDataFromSettings(config,
548 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
549
550 // override VM name as well, it may be different
551 mUserData->s.strName = strName;
552
553 if (SUCCEEDED(rc))
554 {
555 /* At this point the changing of the current state modification
556 * flag is allowed. */
557 allowStateModification();
558
559 /* commit all changes made during the initialization */
560 commit();
561 }
562 }
563
564 /* Confirm a successful initialization when it's the case */
565 if (SUCCEEDED(rc))
566 {
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 {
571 autoInitSpan.setLimited();
572
573 // uninit media from this machine's media registry, or else
574 // reloading the settings will fail
575 mParent->unregisterMachineMedia(getId());
576 }
577 }
578
579 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
580 "rc=%08X\n",
581 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
582 mData->mRegistered, mData->mAccessible, rc));
583
584 LogFlowThisFuncLeave();
585
586 return rc;
587}
588
589/**
590 * Shared code between the various init() implementations.
591 * @param aParent
592 * @return
593 */
594HRESULT Machine::initImpl(VirtualBox *aParent,
595 const Utf8Str &strConfigFile)
596{
597 LogFlowThisFuncEnter();
598
599 AssertReturn(aParent, E_INVALIDARG);
600 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
601
602 HRESULT rc = S_OK;
603
604 /* share the parent weakly */
605 unconst(mParent) = aParent;
606
607 /* allocate the essential machine data structure (the rest will be
608 * allocated later by initDataAndChildObjects() */
609 mData.allocate();
610
611 /* memorize the config file name (as provided) */
612 mData->m_strConfigFile = strConfigFile;
613
614 /* get the full file name */
615 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
616 if (RT_FAILURE(vrc1))
617 return setError(VBOX_E_FILE_ERROR,
618 tr("Invalid machine settings file name '%s' (%Rrc)"),
619 strConfigFile.c_str(),
620 vrc1);
621
622 LogFlowThisFuncLeave();
623
624 return rc;
625}
626
627/**
628 * Tries to create a machine settings file in the path stored in the machine
629 * instance data. Used when a new machine is created to fail gracefully if
630 * the settings file could not be written (e.g. because machine dir is read-only).
631 * @return
632 */
633HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
634{
635 HRESULT rc = S_OK;
636
637 // when we create a new machine, we must be able to create the settings file
638 RTFILE f = NIL_RTFILE;
639 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
640 if ( RT_SUCCESS(vrc)
641 || vrc == VERR_SHARING_VIOLATION
642 )
643 {
644 if (RT_SUCCESS(vrc))
645 RTFileClose(f);
646 if (!fForceOverwrite)
647 rc = setError(VBOX_E_FILE_ERROR,
648 tr("Machine settings file '%s' already exists"),
649 mData->m_strConfigFileFull.c_str());
650 else
651 {
652 /* try to delete the config file, as otherwise the creation
653 * of a new settings file will fail. */
654 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
655 if (RT_FAILURE(vrc2))
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Could not delete the existing settings file '%s' (%Rrc)"),
658 mData->m_strConfigFileFull.c_str(), vrc2);
659 }
660 }
661 else if ( vrc != VERR_FILE_NOT_FOUND
662 && vrc != VERR_PATH_NOT_FOUND
663 )
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Invalid machine settings file name '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(),
667 vrc);
668 return rc;
669}
670
671/**
672 * Initializes the registered machine by loading the settings file.
673 * This method is separated from #init() in order to make it possible to
674 * retry the operation after VirtualBox startup instead of refusing to
675 * startup the whole VirtualBox server in case if the settings file of some
676 * registered VM is invalid or inaccessible.
677 *
678 * @note Must be always called from this object's write lock
679 * (unless called from #init() that doesn't need any locking).
680 * @note Locks the mUSBController method for writing.
681 * @note Subclasses must not call this method.
682 */
683HRESULT Machine::registeredInit()
684{
685 AssertReturn(!isSessionMachine(), E_FAIL);
686 AssertReturn(!isSnapshotMachine(), E_FAIL);
687 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
688 AssertReturn(!mData->mAccessible, E_FAIL);
689
690 HRESULT rc = initDataAndChildObjects();
691
692 if (SUCCEEDED(rc))
693 {
694 /* Temporarily reset the registered flag in order to let setters
695 * potentially called from loadSettings() succeed (isMutable() used in
696 * all setters will return FALSE for a Machine instance if mRegistered
697 * is TRUE). */
698 mData->mRegistered = FALSE;
699
700 try
701 {
702 // load and parse machine XML; this will throw on XML or logic errors
703 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
704
705 if (mData->mUuid != mData->pMachineConfigFile->uuid)
706 throw setError(E_FAIL,
707 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
708 mData->pMachineConfigFile->uuid.raw(),
709 mData->m_strConfigFileFull.c_str(),
710 mData->mUuid.toString().c_str(),
711 mParent->settingsFilePath().c_str());
712
713 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
714 NULL /* const Guid *puuidRegistry */);
715 if (FAILED(rc)) throw rc;
716 }
717 catch (HRESULT err)
718 {
719 /* we assume that error info is set by the thrower */
720 rc = err;
721 }
722 catch (...)
723 {
724 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
725 }
726
727 /* Restore the registered flag (even on failure) */
728 mData->mRegistered = TRUE;
729 }
730
731 if (SUCCEEDED(rc))
732 {
733 /* Set mAccessible to TRUE only if we successfully locked and loaded
734 * the settings file */
735 mData->mAccessible = TRUE;
736
737 /* commit all changes made during loading the settings file */
738 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
739 /// @todo r=klaus for some reason the settings loading logic backs up
740 // the settings, and therefore a commit is needed. Should probably be changed.
741 }
742 else
743 {
744 /* If the machine is registered, then, instead of returning a
745 * failure, we mark it as inaccessible and set the result to
746 * success to give it a try later */
747
748 /* fetch the current error info */
749 mData->mAccessError = com::ErrorInfo();
750 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
751 mData->mUuid.raw(),
752 mData->mAccessError.getText().raw()));
753
754 /* rollback all changes */
755 rollback(false /* aNotify */);
756
757 // uninit media from this machine's media registry, or else
758 // reloading the settings will fail
759 mParent->unregisterMachineMedia(getId());
760
761 /* uninitialize the common part to make sure all data is reset to
762 * default (null) values */
763 uninitDataAndChildObjects();
764
765 rc = S_OK;
766 }
767
768 return rc;
769}
770
771/**
772 * Uninitializes the instance.
773 * Called either from FinalRelease() or by the parent when it gets destroyed.
774 *
775 * @note The caller of this method must make sure that this object
776 * a) doesn't have active callers on the current thread and b) is not locked
777 * by the current thread; otherwise uninit() will hang either a) due to
778 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
779 * a dead-lock caused by this thread waiting for all callers on the other
780 * threads are done but preventing them from doing so by holding a lock.
781 */
782void Machine::uninit()
783{
784 LogFlowThisFuncEnter();
785
786 Assert(!isWriteLockOnCurrentThread());
787
788 Assert(!uRegistryNeedsSaving);
789 if (uRegistryNeedsSaving)
790 {
791 AutoCaller autoCaller(this);
792 if (SUCCEEDED(autoCaller.rc()))
793 {
794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
795 saveSettings(NULL, Machine::SaveS_Force);
796 }
797 }
798
799 /* Enclose the state transition Ready->InUninit->NotReady */
800 AutoUninitSpan autoUninitSpan(this);
801 if (autoUninitSpan.uninitDone())
802 return;
803
804 Assert(!isSnapshotMachine());
805 Assert(!isSessionMachine());
806 Assert(!!mData);
807
808 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
809 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
810
811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
812
813 if (!mData->mSession.mMachine.isNull())
814 {
815 /* Theoretically, this can only happen if the VirtualBox server has been
816 * terminated while there were clients running that owned open direct
817 * sessions. Since in this case we are definitely called by
818 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
819 * won't happen on the client watcher thread (because it does
820 * VirtualBox::addCaller() for the duration of the
821 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
822 * cannot happen until the VirtualBox caller is released). This is
823 * important, because SessionMachine::uninit() cannot correctly operate
824 * after we return from this method (it expects the Machine instance is
825 * still valid). We'll call it ourselves below.
826 */
827 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
828 (SessionMachine*)mData->mSession.mMachine));
829
830 if (Global::IsOnlineOrTransient(mData->mMachineState))
831 {
832 LogWarningThisFunc(("Setting state to Aborted!\n"));
833 /* set machine state using SessionMachine reimplementation */
834 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
835 }
836
837 /*
838 * Uninitialize SessionMachine using public uninit() to indicate
839 * an unexpected uninitialization.
840 */
841 mData->mSession.mMachine->uninit();
842 /* SessionMachine::uninit() must set mSession.mMachine to null */
843 Assert(mData->mSession.mMachine.isNull());
844 }
845
846 // uninit media from this machine's media registry, if they're still there
847 Guid uuidMachine(getId());
848
849 /* the lock is no more necessary (SessionMachine is uninitialized) */
850 alock.release();
851
852 /* XXX This will fail with
853 * "cannot be closed because it is still attached to 1 virtual machines"
854 * because at this point we did not call uninitDataAndChildObjects() yet
855 * and therefore also removeBackReference() for all these mediums was not called! */
856 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
857 mParent->unregisterMachineMedia(uuidMachine);
858
859 // has machine been modified?
860 if (mData->flModifications)
861 {
862 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
863 rollback(false /* aNotify */);
864 }
865
866 if (mData->mAccessible)
867 uninitDataAndChildObjects();
868
869 /* free the essential data structure last */
870 mData.free();
871
872 LogFlowThisFuncLeave();
873}
874
875// IMachine properties
876/////////////////////////////////////////////////////////////////////////////
877
878STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
879{
880 CheckComArgOutPointerValid(aParent);
881
882 AutoLimitedCaller autoCaller(this);
883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
884
885 /* mParent is constant during life time, no need to lock */
886 ComObjPtr<VirtualBox> pVirtualBox(mParent);
887 pVirtualBox.queryInterfaceTo(aParent);
888
889 return S_OK;
890}
891
892STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
893{
894 CheckComArgOutPointerValid(aAccessible);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 LogFlowThisFunc(("ENTER\n"));
900
901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
902
903 HRESULT rc = S_OK;
904
905 if (!mData->mAccessible)
906 {
907 /* try to initialize the VM once more if not accessible */
908
909 AutoReinitSpan autoReinitSpan(this);
910 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
911
912#ifdef DEBUG
913 LogFlowThisFunc(("Dumping media backreferences\n"));
914 mParent->dumpAllBackRefs();
915#endif
916
917 if (mData->pMachineConfigFile)
918 {
919 // reset the XML file to force loadSettings() (called from registeredInit())
920 // to parse it again; the file might have changed
921 delete mData->pMachineConfigFile;
922 mData->pMachineConfigFile = NULL;
923 }
924
925 rc = registeredInit();
926
927 if (SUCCEEDED(rc) && mData->mAccessible)
928 {
929 autoReinitSpan.setSucceeded();
930
931 /* make sure interesting parties will notice the accessibility
932 * state change */
933 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
934 mParent->onMachineDataChange(mData->mUuid);
935 }
936 }
937
938 if (SUCCEEDED(rc))
939 *aAccessible = mData->mAccessible;
940
941 LogFlowThisFuncLeave();
942
943 return rc;
944}
945
946STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
947{
948 CheckComArgOutPointerValid(aAccessError);
949
950 AutoLimitedCaller autoCaller(this);
951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
952
953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
954
955 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
956 {
957 /* return shortly */
958 aAccessError = NULL;
959 return S_OK;
960 }
961
962 HRESULT rc = S_OK;
963
964 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
965 rc = errorInfo.createObject();
966 if (SUCCEEDED(rc))
967 {
968 errorInfo->init(mData->mAccessError.getResultCode(),
969 mData->mAccessError.getInterfaceID().ref(),
970 Utf8Str(mData->mAccessError.getComponent()).c_str(),
971 Utf8Str(mData->mAccessError.getText()));
972 rc = errorInfo.queryInterfaceTo(aAccessError);
973 }
974
975 return rc;
976}
977
978STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
979{
980 CheckComArgOutPointerValid(aName);
981
982 AutoCaller autoCaller(this);
983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
984
985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
986
987 mUserData->s.strName.cloneTo(aName);
988
989 return S_OK;
990}
991
992STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
993{
994 CheckComArgStrNotEmptyOrNull(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 // prohibit setting a UUID only as the machine name, or else it can
1000 // never be found by findMachine()
1001 Guid test(aName);
1002 if (test.isNotEmpty())
1003 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1004
1005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1006
1007 HRESULT rc = checkStateDependency(MutableStateDep);
1008 if (FAILED(rc)) return rc;
1009
1010 setModified(IsModified_MachineData);
1011 mUserData.backup();
1012 mUserData->s.strName = aName;
1013
1014 return S_OK;
1015}
1016
1017STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1018{
1019 CheckComArgOutPointerValid(aDescription);
1020
1021 AutoCaller autoCaller(this);
1022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1023
1024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 mUserData->s.strDescription.cloneTo(aDescription);
1027
1028 return S_OK;
1029}
1030
1031STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1032{
1033 AutoCaller autoCaller(this);
1034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1035
1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 // this can be done in principle in any state as it doesn't affect the VM
1039 // significantly, but play safe by not messing around while complex
1040 // activities are going on
1041 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1042 if (FAILED(rc)) return rc;
1043
1044 setModified(IsModified_MachineData);
1045 mUserData.backup();
1046 mUserData->s.strDescription = aDescription;
1047
1048 return S_OK;
1049}
1050
1051STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1052{
1053 CheckComArgOutPointerValid(aId);
1054
1055 AutoLimitedCaller autoCaller(this);
1056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1057
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059
1060 mData->mUuid.toUtf16().cloneTo(aId);
1061
1062 return S_OK;
1063}
1064
1065STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1066{
1067 CheckComArgOutSafeArrayPointerValid(aGroups);
1068
1069 AutoCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1074 size_t i = 0;
1075 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1076 it != mUserData->s.llGroups.end();
1077 ++it, i++)
1078 {
1079 Bstr tmp = *it;
1080 tmp.cloneTo(&groups[i]);
1081 }
1082 groups.detachTo(ComSafeArrayOutArg(aGroups));
1083
1084 return S_OK;
1085}
1086
1087STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1088{
1089 AutoCaller autoCaller(this);
1090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1091
1092 StringsList llGroups;
1093 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1094 if (FAILED(rc))
1095 return rc;
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 // changing machine groups is possible while the VM is offline
1100 rc = checkStateDependency(OfflineStateDep);
1101 if (FAILED(rc)) return rc;
1102
1103 setModified(IsModified_MachineData);
1104 mUserData.backup();
1105 mUserData->s.llGroups = llGroups;
1106
1107 return S_OK;
1108}
1109
1110STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1111{
1112 CheckComArgOutPointerValid(aOSTypeId);
1113
1114 AutoCaller autoCaller(this);
1115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1116
1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 mUserData->s.strOsType.cloneTo(aOSTypeId);
1120
1121 return S_OK;
1122}
1123
1124STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1125{
1126 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1127
1128 AutoCaller autoCaller(this);
1129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1130
1131 /* look up the object by Id to check it is valid */
1132 ComPtr<IGuestOSType> guestOSType;
1133 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1134 if (FAILED(rc)) return rc;
1135
1136 /* when setting, always use the "etalon" value for consistency -- lookup
1137 * by ID is case-insensitive and the input value may have different case */
1138 Bstr osTypeId;
1139 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1140 if (FAILED(rc)) return rc;
1141
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 rc = checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 setModified(IsModified_MachineData);
1148 mUserData.backup();
1149 mUserData->s.strOsType = osTypeId;
1150
1151 return S_OK;
1152}
1153
1154
1155STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1156{
1157 CheckComArgOutPointerValid(aFirmwareType);
1158
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aFirmwareType = mHWData->mFirmwareType;
1165
1166 return S_OK;
1167}
1168
1169STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1170{
1171 AutoCaller autoCaller(this);
1172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT rc = checkStateDependency(MutableStateDep);
1176 if (FAILED(rc)) return rc;
1177
1178 setModified(IsModified_MachineData);
1179 mHWData.backup();
1180 mHWData->mFirmwareType = aFirmwareType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1186{
1187 CheckComArgOutPointerValid(aKeyboardHIDType);
1188
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1200{
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 HRESULT rc = checkStateDependency(MutableStateDep);
1206 if (FAILED(rc)) return rc;
1207
1208 setModified(IsModified_MachineData);
1209 mHWData.backup();
1210 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1211
1212 return S_OK;
1213}
1214
1215STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1216{
1217 CheckComArgOutPointerValid(aPointingHIDType);
1218
1219 AutoCaller autoCaller(this);
1220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1221
1222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 *aPointingHIDType = mHWData->mPointingHIDType;
1225
1226 return S_OK;
1227}
1228
1229STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1230{
1231 AutoCaller autoCaller(this);
1232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 HRESULT rc = checkStateDependency(MutableStateDep);
1236 if (FAILED(rc)) return rc;
1237
1238 setModified(IsModified_MachineData);
1239 mHWData.backup();
1240 mHWData->mPointingHIDType = aPointingHIDType;
1241
1242 return S_OK;
1243}
1244
1245STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1246{
1247 CheckComArgOutPointerValid(aChipsetType);
1248
1249 AutoCaller autoCaller(this);
1250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1251
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aChipsetType = mHWData->mChipsetType;
1255
1256 return S_OK;
1257}
1258
1259STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1260{
1261 AutoCaller autoCaller(this);
1262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT rc = checkStateDependency(MutableStateDep);
1266 if (FAILED(rc)) return rc;
1267
1268 if (aChipsetType != mHWData->mChipsetType)
1269 {
1270 setModified(IsModified_MachineData);
1271 mHWData.backup();
1272 mHWData->mChipsetType = aChipsetType;
1273
1274 // Resize network adapter array, to be finalized on commit/rollback.
1275 // We must not throw away entries yet, otherwise settings are lost
1276 // without a way to roll back.
1277 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1278 uint32_t oldCount = mNetworkAdapters.size();
1279 if (newCount > oldCount)
1280 {
1281 mNetworkAdapters.resize(newCount);
1282 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1283 {
1284 unconst(mNetworkAdapters[slot]).createObject();
1285 mNetworkAdapters[slot]->init(this, slot);
1286 }
1287 }
1288 }
1289
1290 return S_OK;
1291}
1292
1293STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1294{
1295 CheckComArgOutPointerValid(aHWVersion);
1296
1297 AutoCaller autoCaller(this);
1298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1299
1300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1301
1302 mHWData->mHWVersion.cloneTo(aHWVersion);
1303
1304 return S_OK;
1305}
1306
1307STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1308{
1309 /* check known version */
1310 Utf8Str hwVersion = aHWVersion;
1311 if ( hwVersion.compare("1") != 0
1312 && hwVersion.compare("2") != 0)
1313 return setError(E_INVALIDARG,
1314 tr("Invalid hardware version: %ls\n"), aHWVersion);
1315
1316 AutoCaller autoCaller(this);
1317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1318
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 HRESULT rc = checkStateDependency(MutableStateDep);
1322 if (FAILED(rc)) return rc;
1323
1324 setModified(IsModified_MachineData);
1325 mHWData.backup();
1326 mHWData->mHWVersion = hwVersion;
1327
1328 return S_OK;
1329}
1330
1331STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1332{
1333 CheckComArgOutPointerValid(aUUID);
1334
1335 AutoCaller autoCaller(this);
1336 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1337
1338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1339
1340 if (!mHWData->mHardwareUUID.isEmpty())
1341 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1342 else
1343 mData->mUuid.toUtf16().cloneTo(aUUID);
1344
1345 return S_OK;
1346}
1347
1348STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1349{
1350 Guid hardwareUUID(aUUID);
1351 if (hardwareUUID.isEmpty())
1352 return E_INVALIDARG;
1353
1354 AutoCaller autoCaller(this);
1355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1356
1357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1358
1359 HRESULT rc = checkStateDependency(MutableStateDep);
1360 if (FAILED(rc)) return rc;
1361
1362 setModified(IsModified_MachineData);
1363 mHWData.backup();
1364 if (hardwareUUID == mData->mUuid)
1365 mHWData->mHardwareUUID.clear();
1366 else
1367 mHWData->mHardwareUUID = hardwareUUID;
1368
1369 return S_OK;
1370}
1371
1372STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1373{
1374 CheckComArgOutPointerValid(memorySize);
1375
1376 AutoCaller autoCaller(this);
1377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1378
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 *memorySize = mHWData->mMemorySize;
1382
1383 return S_OK;
1384}
1385
1386STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1387{
1388 /* check RAM limits */
1389 if ( memorySize < MM_RAM_MIN_IN_MB
1390 || memorySize > MM_RAM_MAX_IN_MB
1391 )
1392 return setError(E_INVALIDARG,
1393 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1394 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1395
1396 AutoCaller autoCaller(this);
1397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1398
1399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 HRESULT rc = checkStateDependency(MutableStateDep);
1402 if (FAILED(rc)) return rc;
1403
1404 setModified(IsModified_MachineData);
1405 mHWData.backup();
1406 mHWData->mMemorySize = memorySize;
1407
1408 return S_OK;
1409}
1410
1411STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1412{
1413 CheckComArgOutPointerValid(CPUCount);
1414
1415 AutoCaller autoCaller(this);
1416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1417
1418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 *CPUCount = mHWData->mCPUCount;
1421
1422 return S_OK;
1423}
1424
1425STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1426{
1427 /* check CPU limits */
1428 if ( CPUCount < SchemaDefs::MinCPUCount
1429 || CPUCount > SchemaDefs::MaxCPUCount
1430 )
1431 return setError(E_INVALIDARG,
1432 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1433 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1434
1435 AutoCaller autoCaller(this);
1436 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1437
1438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1441 if (mHWData->mCPUHotPlugEnabled)
1442 {
1443 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1444 {
1445 if (mHWData->mCPUAttached[idx])
1446 return setError(E_INVALIDARG,
1447 tr("There is still a CPU attached to socket %lu."
1448 "Detach the CPU before removing the socket"),
1449 CPUCount, idx+1);
1450 }
1451 }
1452
1453 HRESULT rc = checkStateDependency(MutableStateDep);
1454 if (FAILED(rc)) return rc;
1455
1456 setModified(IsModified_MachineData);
1457 mHWData.backup();
1458 mHWData->mCPUCount = CPUCount;
1459
1460 return S_OK;
1461}
1462
1463STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1464{
1465 CheckComArgOutPointerValid(aExecutionCap);
1466
1467 AutoCaller autoCaller(this);
1468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1469
1470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 *aExecutionCap = mHWData->mCpuExecutionCap;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1478{
1479 HRESULT rc = S_OK;
1480
1481 /* check throttle limits */
1482 if ( aExecutionCap < 1
1483 || aExecutionCap > 100
1484 )
1485 return setError(E_INVALIDARG,
1486 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1487 aExecutionCap, 1, 100);
1488
1489 AutoCaller autoCaller(this);
1490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1491
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 alock.release();
1495 rc = onCPUExecutionCapChange(aExecutionCap);
1496 alock.acquire();
1497 if (FAILED(rc)) return rc;
1498
1499 setModified(IsModified_MachineData);
1500 mHWData.backup();
1501 mHWData->mCpuExecutionCap = aExecutionCap;
1502
1503 /* Save settings if online - todo why is this required?? */
1504 if (Global::IsOnline(mData->mMachineState))
1505 saveSettings(NULL);
1506
1507 return S_OK;
1508}
1509
1510
1511STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1512{
1513 CheckComArgOutPointerValid(enabled);
1514
1515 AutoCaller autoCaller(this);
1516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1517
1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1519
1520 *enabled = mHWData->mCPUHotPlugEnabled;
1521
1522 return S_OK;
1523}
1524
1525STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1526{
1527 HRESULT rc = S_OK;
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 rc = checkStateDependency(MutableStateDep);
1535 if (FAILED(rc)) return rc;
1536
1537 if (mHWData->mCPUHotPlugEnabled != enabled)
1538 {
1539 if (enabled)
1540 {
1541 setModified(IsModified_MachineData);
1542 mHWData.backup();
1543
1544 /* Add the amount of CPUs currently attached */
1545 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1546 {
1547 mHWData->mCPUAttached[i] = true;
1548 }
1549 }
1550 else
1551 {
1552 /*
1553 * We can disable hotplug only if the amount of maximum CPUs is equal
1554 * to the amount of attached CPUs
1555 */
1556 unsigned cCpusAttached = 0;
1557 unsigned iHighestId = 0;
1558
1559 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1560 {
1561 if (mHWData->mCPUAttached[i])
1562 {
1563 cCpusAttached++;
1564 iHighestId = i;
1565 }
1566 }
1567
1568 if ( (cCpusAttached != mHWData->mCPUCount)
1569 || (iHighestId >= mHWData->mCPUCount))
1570 return setError(E_INVALIDARG,
1571 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1572
1573 setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 }
1576 }
1577
1578 mHWData->mCPUHotPlugEnabled = enabled;
1579
1580 return rc;
1581}
1582
1583STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1584{
1585#ifdef VBOX_WITH_USB_CARDREADER
1586 CheckComArgOutPointerValid(enabled);
1587
1588 AutoCaller autoCaller(this);
1589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1590
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1594
1595 return S_OK;
1596#else
1597 NOREF(enabled);
1598 return E_NOTIMPL;
1599#endif
1600}
1601
1602STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1603{
1604#ifdef VBOX_WITH_USB_CARDREADER
1605 AutoCaller autoCaller(this);
1606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1608
1609 HRESULT rc = checkStateDependency(MutableStateDep);
1610 if (FAILED(rc)) return rc;
1611
1612 setModified(IsModified_MachineData);
1613 mHWData.backup();
1614 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1615
1616 return S_OK;
1617#else
1618 NOREF(enabled);
1619 return E_NOTIMPL;
1620#endif
1621}
1622
1623STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1624{
1625 NOREF(enabled);
1626 return E_NOTIMPL;
1627}
1628
1629STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1630{
1631 NOREF(enabled);
1632 return E_NOTIMPL;
1633}
1634
1635STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1636{
1637 CheckComArgOutPointerValid(enabled);
1638
1639 AutoCaller autoCaller(this);
1640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *enabled = mHWData->mHPETEnabled;
1644
1645 return S_OK;
1646}
1647
1648STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1649{
1650 HRESULT rc = S_OK;
1651
1652 AutoCaller autoCaller(this);
1653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 rc = checkStateDependency(MutableStateDep);
1657 if (FAILED(rc)) return rc;
1658
1659 setModified(IsModified_MachineData);
1660 mHWData.backup();
1661
1662 mHWData->mHPETEnabled = enabled;
1663
1664 return rc;
1665}
1666
1667STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled)
1668{
1669 AutoCaller autoCaller(this);
1670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1671
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *fEnabled = mHWData->mVideoCaptureEnabled;
1675 return S_OK;
1676}
1677
1678STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1679{
1680 AutoCaller autoCaller(this);
1681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1682
1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1684 mHWData->mVideoCaptureEnabled = fEnabled;
1685 return S_OK;
1686}
1687
1688STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1689{
1690 AutoCaller autoCaller(this);
1691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1692
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 mHWData->mVideoCaptureFile.cloneTo(apFile);
1695 return S_OK;
1696}
1697
1698STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1699{
1700 Utf8Str strFile(aFile);
1701 AutoCaller autoCaller(this);
1702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705 if(strFile.isEmpty())
1706 strFile = "VideoCap.webm";
1707 mHWData->mVideoCaptureFile = strFile;
1708 return S_OK;
1709}
1710
1711
1712STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1713{
1714 AutoCaller autoCaller(this);
1715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1716
1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1718 *ulHorzRes = mHWData->mVideoCaptureWidth;
1719 return S_OK;
1720}
1721
1722STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1723{
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc()))
1726 {
1727 LogFlow(("Autolocked failed\n"));
1728 return autoCaller.rc();
1729 }
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732 mHWData->mVideoCaptureWidth = ulHorzRes;
1733 return S_OK;
1734}
1735
1736STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1737{
1738 AutoCaller autoCaller(this);
1739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1740
1741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1742 *ulVertRes = mHWData->mVideoCaptureHeight;
1743 return S_OK;
1744}
1745
1746STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1747{
1748 AutoCaller autoCaller(this);
1749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1750
1751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1752 mHWData->mVideoCaptureHeight = ulVertRes;
1753 return S_OK;
1754}
1755
1756STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1757{
1758 CheckComArgOutPointerValid(memorySize);
1759
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 *memorySize = mHWData->mVRAMSize;
1766
1767 return S_OK;
1768}
1769
1770STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1771{
1772 /* check VRAM limits */
1773 if (memorySize < SchemaDefs::MinGuestVRAM ||
1774 memorySize > SchemaDefs::MaxGuestVRAM)
1775 return setError(E_INVALIDARG,
1776 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1777 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1778
1779 AutoCaller autoCaller(this);
1780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1781
1782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 HRESULT rc = checkStateDependency(MutableStateDep);
1785 if (FAILED(rc)) return rc;
1786
1787 setModified(IsModified_MachineData);
1788 mHWData.backup();
1789 mHWData->mVRAMSize = memorySize;
1790
1791 return S_OK;
1792}
1793
1794/** @todo this method should not be public */
1795STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1796{
1797 CheckComArgOutPointerValid(memoryBalloonSize);
1798
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1805
1806 return S_OK;
1807}
1808
1809/**
1810 * Set the memory balloon size.
1811 *
1812 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1813 * we have to make sure that we never call IGuest from here.
1814 */
1815STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1816{
1817 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1818#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1819 /* check limits */
1820 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1821 return setError(E_INVALIDARG,
1822 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1823 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1824
1825 AutoCaller autoCaller(this);
1826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1827
1828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1829
1830 setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1833
1834 return S_OK;
1835#else
1836 NOREF(memoryBalloonSize);
1837 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1838#endif
1839}
1840
1841STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1842{
1843 CheckComArgOutPointerValid(enabled);
1844
1845 AutoCaller autoCaller(this);
1846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1847
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 *enabled = mHWData->mPageFusionEnabled;
1851 return S_OK;
1852}
1853
1854STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1855{
1856#ifdef VBOX_WITH_PAGE_SHARING
1857 AutoCaller autoCaller(this);
1858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1859
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1863 setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mPageFusionEnabled = enabled;
1866 return S_OK;
1867#else
1868 NOREF(enabled);
1869 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1870#endif
1871}
1872
1873STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1874{
1875 CheckComArgOutPointerValid(enabled);
1876
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 *enabled = mHWData->mAccelerate3DEnabled;
1883
1884 return S_OK;
1885}
1886
1887STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1888{
1889 AutoCaller autoCaller(this);
1890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1891
1892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1893
1894 HRESULT rc = checkStateDependency(MutableStateDep);
1895 if (FAILED(rc)) return rc;
1896
1897 /** @todo check validity! */
1898
1899 setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mAccelerate3DEnabled = enable;
1902
1903 return S_OK;
1904}
1905
1906
1907STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1908{
1909 CheckComArgOutPointerValid(enabled);
1910
1911 AutoCaller autoCaller(this);
1912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1913
1914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1915
1916 *enabled = mHWData->mAccelerate2DVideoEnabled;
1917
1918 return S_OK;
1919}
1920
1921STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1922{
1923 AutoCaller autoCaller(this);
1924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1925
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 HRESULT rc = checkStateDependency(MutableStateDep);
1929 if (FAILED(rc)) return rc;
1930
1931 /** @todo check validity! */
1932
1933 setModified(IsModified_MachineData);
1934 mHWData.backup();
1935 mHWData->mAccelerate2DVideoEnabled = enable;
1936
1937 return S_OK;
1938}
1939
1940STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1941{
1942 CheckComArgOutPointerValid(monitorCount);
1943
1944 AutoCaller autoCaller(this);
1945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1946
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 *monitorCount = mHWData->mMonitorCount;
1950
1951 return S_OK;
1952}
1953
1954STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1955{
1956 /* make sure monitor count is a sensible number */
1957 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1958 return setError(E_INVALIDARG,
1959 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1960 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1961
1962 AutoCaller autoCaller(this);
1963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1964
1965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
1967 HRESULT rc = checkStateDependency(MutableStateDep);
1968 if (FAILED(rc)) return rc;
1969
1970 setModified(IsModified_MachineData);
1971 mHWData.backup();
1972 mHWData->mMonitorCount = monitorCount;
1973
1974 return S_OK;
1975}
1976
1977STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1978{
1979 CheckComArgOutPointerValid(biosSettings);
1980
1981 AutoCaller autoCaller(this);
1982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1983
1984 /* mBIOSSettings is constant during life time, no need to lock */
1985 mBIOSSettings.queryInterfaceTo(biosSettings);
1986
1987 return S_OK;
1988}
1989
1990STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1991{
1992 CheckComArgOutPointerValid(aVal);
1993
1994 AutoCaller autoCaller(this);
1995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1996
1997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 switch(property)
2000 {
2001 case CPUPropertyType_PAE:
2002 *aVal = mHWData->mPAEEnabled;
2003 break;
2004
2005 case CPUPropertyType_Synthetic:
2006 *aVal = mHWData->mSyntheticCpu;
2007 break;
2008
2009 default:
2010 return E_INVALIDARG;
2011 }
2012 return S_OK;
2013}
2014
2015STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2016{
2017 AutoCaller autoCaller(this);
2018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2019
2020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 HRESULT rc = checkStateDependency(MutableStateDep);
2023 if (FAILED(rc)) return rc;
2024
2025 switch(property)
2026 {
2027 case CPUPropertyType_PAE:
2028 setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mPAEEnabled = !!aVal;
2031 break;
2032
2033 case CPUPropertyType_Synthetic:
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mSyntheticCpu = !!aVal;
2037 break;
2038
2039 default:
2040 return E_INVALIDARG;
2041 }
2042 return S_OK;
2043}
2044
2045STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2046{
2047 CheckComArgOutPointerValid(aValEax);
2048 CheckComArgOutPointerValid(aValEbx);
2049 CheckComArgOutPointerValid(aValEcx);
2050 CheckComArgOutPointerValid(aValEdx);
2051
2052 AutoCaller autoCaller(this);
2053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2054
2055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2056
2057 switch(aId)
2058 {
2059 case 0x0:
2060 case 0x1:
2061 case 0x2:
2062 case 0x3:
2063 case 0x4:
2064 case 0x5:
2065 case 0x6:
2066 case 0x7:
2067 case 0x8:
2068 case 0x9:
2069 case 0xA:
2070 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2071 return E_INVALIDARG;
2072
2073 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2074 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2075 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2076 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2077 break;
2078
2079 case 0x80000000:
2080 case 0x80000001:
2081 case 0x80000002:
2082 case 0x80000003:
2083 case 0x80000004:
2084 case 0x80000005:
2085 case 0x80000006:
2086 case 0x80000007:
2087 case 0x80000008:
2088 case 0x80000009:
2089 case 0x8000000A:
2090 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2091 return E_INVALIDARG;
2092
2093 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2094 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2095 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2096 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2097 break;
2098
2099 default:
2100 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2101 }
2102 return S_OK;
2103}
2104
2105STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2106{
2107 AutoCaller autoCaller(this);
2108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2109
2110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 HRESULT rc = checkStateDependency(MutableStateDep);
2113 if (FAILED(rc)) return rc;
2114
2115 switch(aId)
2116 {
2117 case 0x0:
2118 case 0x1:
2119 case 0x2:
2120 case 0x3:
2121 case 0x4:
2122 case 0x5:
2123 case 0x6:
2124 case 0x7:
2125 case 0x8:
2126 case 0x9:
2127 case 0xA:
2128 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2129 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2130 setModified(IsModified_MachineData);
2131 mHWData.backup();
2132 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2133 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2134 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2135 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2136 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2137 break;
2138
2139 case 0x80000000:
2140 case 0x80000001:
2141 case 0x80000002:
2142 case 0x80000003:
2143 case 0x80000004:
2144 case 0x80000005:
2145 case 0x80000006:
2146 case 0x80000007:
2147 case 0x80000008:
2148 case 0x80000009:
2149 case 0x8000000A:
2150 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2151 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2152 setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2155 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2157 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2158 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2159 break;
2160
2161 default:
2162 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2163 }
2164 return S_OK;
2165}
2166
2167STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2168{
2169 AutoCaller autoCaller(this);
2170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2171
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 switch(aId)
2178 {
2179 case 0x0:
2180 case 0x1:
2181 case 0x2:
2182 case 0x3:
2183 case 0x4:
2184 case 0x5:
2185 case 0x6:
2186 case 0x7:
2187 case 0x8:
2188 case 0x9:
2189 case 0xA:
2190 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2191 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2192 setModified(IsModified_MachineData);
2193 mHWData.backup();
2194 /* Invalidate leaf. */
2195 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2196 break;
2197
2198 case 0x80000000:
2199 case 0x80000001:
2200 case 0x80000002:
2201 case 0x80000003:
2202 case 0x80000004:
2203 case 0x80000005:
2204 case 0x80000006:
2205 case 0x80000007:
2206 case 0x80000008:
2207 case 0x80000009:
2208 case 0x8000000A:
2209 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2210 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2211 setModified(IsModified_MachineData);
2212 mHWData.backup();
2213 /* Invalidate leaf. */
2214 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2215 break;
2216
2217 default:
2218 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2219 }
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2224{
2225 AutoCaller autoCaller(this);
2226 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2227
2228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2229
2230 HRESULT rc = checkStateDependency(MutableStateDep);
2231 if (FAILED(rc)) return rc;
2232
2233 setModified(IsModified_MachineData);
2234 mHWData.backup();
2235
2236 /* Invalidate all standard leafs. */
2237 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2238 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2239
2240 /* Invalidate all extended leafs. */
2241 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2242 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2243
2244 return S_OK;
2245}
2246
2247STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2248{
2249 CheckComArgOutPointerValid(aVal);
2250
2251 AutoCaller autoCaller(this);
2252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2253
2254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2255
2256 switch(property)
2257 {
2258 case HWVirtExPropertyType_Enabled:
2259 *aVal = mHWData->mHWVirtExEnabled;
2260 break;
2261
2262 case HWVirtExPropertyType_Exclusive:
2263 *aVal = mHWData->mHWVirtExExclusive;
2264 break;
2265
2266 case HWVirtExPropertyType_VPID:
2267 *aVal = mHWData->mHWVirtExVPIDEnabled;
2268 break;
2269
2270 case HWVirtExPropertyType_NestedPaging:
2271 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2272 break;
2273
2274 case HWVirtExPropertyType_LargePages:
2275 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2276#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2277 *aVal = FALSE;
2278#endif
2279 break;
2280
2281 case HWVirtExPropertyType_Force:
2282 *aVal = mHWData->mHWVirtExForceEnabled;
2283 break;
2284
2285 default:
2286 return E_INVALIDARG;
2287 }
2288 return S_OK;
2289}
2290
2291STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2292{
2293 AutoCaller autoCaller(this);
2294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2295
2296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2297
2298 HRESULT rc = checkStateDependency(MutableStateDep);
2299 if (FAILED(rc)) return rc;
2300
2301 switch(property)
2302 {
2303 case HWVirtExPropertyType_Enabled:
2304 setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mHWVirtExEnabled = !!aVal;
2307 break;
2308
2309 case HWVirtExPropertyType_Exclusive:
2310 setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mHWVirtExExclusive = !!aVal;
2313 break;
2314
2315 case HWVirtExPropertyType_VPID:
2316 setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2319 break;
2320
2321 case HWVirtExPropertyType_NestedPaging:
2322 setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2325 break;
2326
2327 case HWVirtExPropertyType_LargePages:
2328 setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2331 break;
2332
2333 case HWVirtExPropertyType_Force:
2334 setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mHWVirtExForceEnabled = !!aVal;
2337 break;
2338
2339 default:
2340 return E_INVALIDARG;
2341 }
2342
2343 return S_OK;
2344}
2345
2346STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2347{
2348 CheckComArgOutPointerValid(aSnapshotFolder);
2349
2350 AutoCaller autoCaller(this);
2351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2352
2353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 Utf8Str strFullSnapshotFolder;
2356 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2357 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2358
2359 return S_OK;
2360}
2361
2362STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2363{
2364 /* @todo (r=dmik):
2365 * 1. Allow to change the name of the snapshot folder containing snapshots
2366 * 2. Rename the folder on disk instead of just changing the property
2367 * value (to be smart and not to leave garbage). Note that it cannot be
2368 * done here because the change may be rolled back. Thus, the right
2369 * place is #saveSettings().
2370 */
2371
2372 AutoCaller autoCaller(this);
2373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2374
2375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2376
2377 HRESULT rc = checkStateDependency(MutableStateDep);
2378 if (FAILED(rc)) return rc;
2379
2380 if (!mData->mCurrentSnapshot.isNull())
2381 return setError(E_FAIL,
2382 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2383
2384 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2385
2386 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2387 if (strSnapshotFolder.isEmpty())
2388 strSnapshotFolder = "Snapshots";
2389 int vrc = calculateFullPath(strSnapshotFolder,
2390 strSnapshotFolder);
2391 if (RT_FAILURE(vrc))
2392 return setError(E_FAIL,
2393 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2394 aSnapshotFolder, vrc);
2395
2396 setModified(IsModified_MachineData);
2397 mUserData.backup();
2398
2399 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2400
2401 return S_OK;
2402}
2403
2404STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2405{
2406 CheckComArgOutSafeArrayPointerValid(aAttachments);
2407
2408 AutoCaller autoCaller(this);
2409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2410
2411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2414 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2415
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2420{
2421 CheckComArgOutPointerValid(vrdeServer);
2422
2423 AutoCaller autoCaller(this);
2424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2425
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 Assert(!!mVRDEServer);
2429 mVRDEServer.queryInterfaceTo(vrdeServer);
2430
2431 return S_OK;
2432}
2433
2434STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2435{
2436 CheckComArgOutPointerValid(audioAdapter);
2437
2438 AutoCaller autoCaller(this);
2439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2440
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 mAudioAdapter.queryInterfaceTo(audioAdapter);
2444 return S_OK;
2445}
2446
2447STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2448{
2449#ifdef VBOX_WITH_VUSB
2450 CheckComArgOutPointerValid(aUSBController);
2451
2452 AutoCaller autoCaller(this);
2453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2454
2455 clearError();
2456 MultiResult rc(S_OK);
2457
2458# ifdef VBOX_WITH_USB
2459 rc = mParent->host()->checkUSBProxyService();
2460 if (FAILED(rc)) return rc;
2461# endif
2462
2463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 return rc = mUSBController.queryInterfaceTo(aUSBController);
2466#else
2467 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2468 * extended error info to indicate that USB is simply not available
2469 * (w/o treating it as a failure), for example, as in OSE */
2470 NOREF(aUSBController);
2471 ReturnComNotImplemented();
2472#endif /* VBOX_WITH_VUSB */
2473}
2474
2475STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2476{
2477 CheckComArgOutPointerValid(aFilePath);
2478
2479 AutoLimitedCaller autoCaller(this);
2480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2481
2482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 mData->m_strConfigFileFull.cloneTo(aFilePath);
2485 return S_OK;
2486}
2487
2488STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2489{
2490 CheckComArgOutPointerValid(aModified);
2491
2492 AutoCaller autoCaller(this);
2493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2494
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 HRESULT rc = checkStateDependency(MutableStateDep);
2498 if (FAILED(rc)) return rc;
2499
2500 if (!mData->pMachineConfigFile->fileExists())
2501 // this is a new machine, and no config file exists yet:
2502 *aModified = TRUE;
2503 else
2504 *aModified = (mData->flModifications != 0);
2505
2506 return S_OK;
2507}
2508
2509STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2510{
2511 CheckComArgOutPointerValid(aSessionState);
2512
2513 AutoCaller autoCaller(this);
2514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 *aSessionState = mData->mSession.mState;
2519
2520 return S_OK;
2521}
2522
2523STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2524{
2525 CheckComArgOutPointerValid(aSessionType);
2526
2527 AutoCaller autoCaller(this);
2528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2529
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 mData->mSession.mType.cloneTo(aSessionType);
2533
2534 return S_OK;
2535}
2536
2537STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2538{
2539 CheckComArgOutPointerValid(aSessionPID);
2540
2541 AutoCaller autoCaller(this);
2542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2543
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 *aSessionPID = mData->mSession.mPID;
2547
2548 return S_OK;
2549}
2550
2551STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2552{
2553 CheckComArgOutPointerValid(machineState);
2554
2555 AutoCaller autoCaller(this);
2556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2557
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 *machineState = mData->mMachineState;
2561
2562 return S_OK;
2563}
2564
2565STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2566{
2567 CheckComArgOutPointerValid(aLastStateChange);
2568
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2575
2576 return S_OK;
2577}
2578
2579STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2580{
2581 CheckComArgOutPointerValid(aStateFilePath);
2582
2583 AutoCaller autoCaller(this);
2584 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2585
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2589
2590 return S_OK;
2591}
2592
2593STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2594{
2595 CheckComArgOutPointerValid(aLogFolder);
2596
2597 AutoCaller autoCaller(this);
2598 AssertComRCReturnRC(autoCaller.rc());
2599
2600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602 Utf8Str logFolder;
2603 getLogFolder(logFolder);
2604 logFolder.cloneTo(aLogFolder);
2605
2606 return S_OK;
2607}
2608
2609STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2610{
2611 CheckComArgOutPointerValid(aCurrentSnapshot);
2612
2613 AutoCaller autoCaller(this);
2614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2615
2616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2619
2620 return S_OK;
2621}
2622
2623STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2624{
2625 CheckComArgOutPointerValid(aSnapshotCount);
2626
2627 AutoCaller autoCaller(this);
2628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2633 ? 0
2634 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2635
2636 return S_OK;
2637}
2638
2639STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2640{
2641 CheckComArgOutPointerValid(aCurrentStateModified);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 /* Note: for machines with no snapshots, we always return FALSE
2649 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2650 * reasons :) */
2651
2652 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2653 ? FALSE
2654 : mData->mCurrentStateModified;
2655
2656 return S_OK;
2657}
2658
2659STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2660{
2661 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2662
2663 AutoCaller autoCaller(this);
2664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2669 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2670
2671 return S_OK;
2672}
2673
2674STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2675{
2676 CheckComArgOutPointerValid(aClipboardMode);
2677
2678 AutoCaller autoCaller(this);
2679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2680
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 *aClipboardMode = mHWData->mClipboardMode;
2684
2685 return S_OK;
2686}
2687
2688STDMETHODIMP
2689Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2690{
2691 HRESULT rc = S_OK;
2692
2693 AutoCaller autoCaller(this);
2694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2695
2696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 alock.release();
2699 rc = onClipboardModeChange(aClipboardMode);
2700 alock.acquire();
2701 if (FAILED(rc)) return rc;
2702
2703 setModified(IsModified_MachineData);
2704 mHWData.backup();
2705 mHWData->mClipboardMode = aClipboardMode;
2706
2707 /* Save settings if online - todo why is this required?? */
2708 if (Global::IsOnline(mData->mMachineState))
2709 saveSettings(NULL);
2710
2711 return S_OK;
2712}
2713
2714STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2715{
2716 CheckComArgOutPointerValid(aDragAndDropMode);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aDragAndDropMode = mHWData->mDragAndDropMode;
2724
2725 return S_OK;
2726}
2727
2728STDMETHODIMP
2729Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2730{
2731 HRESULT rc = S_OK;
2732
2733 AutoCaller autoCaller(this);
2734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2735
2736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 alock.release();
2739 rc = onDragAndDropModeChange(aDragAndDropMode);
2740 alock.acquire();
2741 if (FAILED(rc)) return rc;
2742
2743 setModified(IsModified_MachineData);
2744 mHWData.backup();
2745 mHWData->mDragAndDropMode = aDragAndDropMode;
2746
2747 /* Save settings if online - todo why is this required?? */
2748 if (Global::IsOnline(mData->mMachineState))
2749 saveSettings(NULL);
2750
2751 return S_OK;
2752}
2753
2754STDMETHODIMP
2755Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2756{
2757 CheckComArgOutPointerValid(aPatterns);
2758
2759 AutoCaller autoCaller(this);
2760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2761
2762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2763
2764 try
2765 {
2766 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2767 }
2768 catch (...)
2769 {
2770 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2771 }
2772
2773 return S_OK;
2774}
2775
2776STDMETHODIMP
2777Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2778{
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 HRESULT rc = checkStateDependency(MutableStateDep);
2785 if (FAILED(rc)) return rc;
2786
2787 setModified(IsModified_MachineData);
2788 mHWData.backup();
2789 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2790 return rc;
2791}
2792
2793STDMETHODIMP
2794Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2795{
2796 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2797
2798 AutoCaller autoCaller(this);
2799 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2800
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2804 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2805
2806 return S_OK;
2807}
2808
2809STDMETHODIMP
2810Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2811{
2812 CheckComArgOutPointerValid(aEnabled);
2813
2814 AutoCaller autoCaller(this);
2815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2816
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aEnabled = mUserData->s.fTeleporterEnabled;
2820
2821 return S_OK;
2822}
2823
2824STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2825{
2826 AutoCaller autoCaller(this);
2827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2828
2829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 /* Only allow it to be set to true when PoweredOff or Aborted.
2832 (Clearing it is always permitted.) */
2833 if ( aEnabled
2834 && mData->mRegistered
2835 && ( !isSessionMachine()
2836 || ( mData->mMachineState != MachineState_PoweredOff
2837 && mData->mMachineState != MachineState_Teleported
2838 && mData->mMachineState != MachineState_Aborted
2839 )
2840 )
2841 )
2842 return setError(VBOX_E_INVALID_VM_STATE,
2843 tr("The machine is not powered off (state is %s)"),
2844 Global::stringifyMachineState(mData->mMachineState));
2845
2846 setModified(IsModified_MachineData);
2847 mUserData.backup();
2848 mUserData->s.fTeleporterEnabled = !!aEnabled;
2849
2850 return S_OK;
2851}
2852
2853STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2854{
2855 CheckComArgOutPointerValid(aPort);
2856
2857 AutoCaller autoCaller(this);
2858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2859
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2863
2864 return S_OK;
2865}
2866
2867STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2868{
2869 if (aPort >= _64K)
2870 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2871
2872 AutoCaller autoCaller(this);
2873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2874
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 HRESULT rc = checkStateDependency(MutableStateDep);
2878 if (FAILED(rc)) return rc;
2879
2880 setModified(IsModified_MachineData);
2881 mUserData.backup();
2882 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2883
2884 return S_OK;
2885}
2886
2887STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2888{
2889 CheckComArgOutPointerValid(aAddress);
2890
2891 AutoCaller autoCaller(this);
2892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2893
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2897
2898 return S_OK;
2899}
2900
2901STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2902{
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.strTeleporterAddress = aAddress;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2919{
2920 CheckComArgOutPointerValid(aPassword);
2921
2922 AutoCaller autoCaller(this);
2923 HRESULT hrc = autoCaller.rc();
2924 if (SUCCEEDED(hrc))
2925 {
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2928 }
2929
2930 return hrc;
2931}
2932
2933STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2934{
2935 /*
2936 * Hash the password first.
2937 */
2938 Utf8Str strPassword(aPassword);
2939 if (!strPassword.isEmpty())
2940 {
2941 if (VBoxIsPasswordHashed(&strPassword))
2942 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2943 VBoxHashPassword(&strPassword);
2944 }
2945
2946 /*
2947 * Do the update.
2948 */
2949 AutoCaller autoCaller(this);
2950 HRESULT hrc = autoCaller.rc();
2951 if (SUCCEEDED(hrc))
2952 {
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 hrc = checkStateDependency(MutableStateDep);
2955 if (SUCCEEDED(hrc))
2956 {
2957 setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterPassword = strPassword;
2960 }
2961 }
2962
2963 return hrc;
2964}
2965
2966STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2967{
2968 CheckComArgOutPointerValid(aState);
2969
2970 AutoCaller autoCaller(this);
2971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2972
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 *aState = mUserData->s.enmFaultToleranceState;
2976 return S_OK;
2977}
2978
2979STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2980{
2981 AutoCaller autoCaller(this);
2982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2983
2984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 /* @todo deal with running state change. */
2987 HRESULT rc = checkStateDependency(MutableStateDep);
2988 if (FAILED(rc)) return rc;
2989
2990 setModified(IsModified_MachineData);
2991 mUserData.backup();
2992 mUserData->s.enmFaultToleranceState = aState;
2993 return S_OK;
2994}
2995
2996STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2997{
2998 CheckComArgOutPointerValid(aAddress);
2999
3000 AutoCaller autoCaller(this);
3001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3002
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3010{
3011 AutoCaller autoCaller(this);
3012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3013
3014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 /* @todo deal with running state change. */
3017 HRESULT rc = checkStateDependency(MutableStateDep);
3018 if (FAILED(rc)) return rc;
3019
3020 setModified(IsModified_MachineData);
3021 mUserData.backup();
3022 mUserData->s.strFaultToleranceAddress = aAddress;
3023 return S_OK;
3024}
3025
3026STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3027{
3028 CheckComArgOutPointerValid(aPort);
3029
3030 AutoCaller autoCaller(this);
3031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3032
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 *aPort = mUserData->s.uFaultTolerancePort;
3036 return S_OK;
3037}
3038
3039STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3040{
3041 AutoCaller autoCaller(this);
3042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3043
3044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 /* @todo deal with running state change. */
3047 HRESULT rc = checkStateDependency(MutableStateDep);
3048 if (FAILED(rc)) return rc;
3049
3050 setModified(IsModified_MachineData);
3051 mUserData.backup();
3052 mUserData->s.uFaultTolerancePort = aPort;
3053 return S_OK;
3054}
3055
3056STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3057{
3058 CheckComArgOutPointerValid(aPassword);
3059
3060 AutoCaller autoCaller(this);
3061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3062
3063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3066
3067 return S_OK;
3068}
3069
3070STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3071{
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 /* @todo deal with running state change. */
3078 HRESULT rc = checkStateDependency(MutableStateDep);
3079 if (FAILED(rc)) return rc;
3080
3081 setModified(IsModified_MachineData);
3082 mUserData.backup();
3083 mUserData->s.strFaultTolerancePassword = aPassword;
3084
3085 return S_OK;
3086}
3087
3088STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3089{
3090 CheckComArgOutPointerValid(aInterval);
3091
3092 AutoCaller autoCaller(this);
3093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3094
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096
3097 *aInterval = mUserData->s.uFaultToleranceInterval;
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 /* @todo deal with running state change. */
3109 HRESULT rc = checkStateDependency(MutableStateDep);
3110 if (FAILED(rc)) return rc;
3111
3112 setModified(IsModified_MachineData);
3113 mUserData.backup();
3114 mUserData->s.uFaultToleranceInterval = aInterval;
3115 return S_OK;
3116}
3117
3118STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3119{
3120 CheckComArgOutPointerValid(aEnabled);
3121
3122 AutoCaller autoCaller(this);
3123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3124
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 *aEnabled = mUserData->s.fRTCUseUTC;
3128
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3133{
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* Only allow it to be set to true when PoweredOff or Aborted.
3140 (Clearing it is always permitted.) */
3141 if ( aEnabled
3142 && mData->mRegistered
3143 && ( !isSessionMachine()
3144 || ( mData->mMachineState != MachineState_PoweredOff
3145 && mData->mMachineState != MachineState_Teleported
3146 && mData->mMachineState != MachineState_Aborted
3147 )
3148 )
3149 )
3150 return setError(VBOX_E_INVALID_VM_STATE,
3151 tr("The machine is not powered off (state is %s)"),
3152 Global::stringifyMachineState(mData->mMachineState));
3153
3154 setModified(IsModified_MachineData);
3155 mUserData.backup();
3156 mUserData->s.fRTCUseUTC = !!aEnabled;
3157
3158 return S_OK;
3159}
3160
3161STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3162{
3163 CheckComArgOutPointerValid(aEnabled);
3164
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 *aEnabled = mHWData->mIOCacheEnabled;
3171
3172 return S_OK;
3173}
3174
3175STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3176{
3177 AutoCaller autoCaller(this);
3178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheEnabled = aEnabled;
3188
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3193{
3194 CheckComArgOutPointerValid(aIOCacheSize);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 *aIOCacheSize = mHWData->mIOCacheSize;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 HRESULT rc = checkStateDependency(MutableStateDep);
3214 if (FAILED(rc)) return rc;
3215
3216 setModified(IsModified_MachineData);
3217 mHWData.backup();
3218 mHWData->mIOCacheSize = aIOCacheSize;
3219
3220 return S_OK;
3221}
3222
3223
3224/**
3225 * @note Locks objects!
3226 */
3227STDMETHODIMP Machine::LockMachine(ISession *aSession,
3228 LockType_T lockType)
3229{
3230 CheckComArgNotNull(aSession);
3231
3232 AutoCaller autoCaller(this);
3233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3234
3235 /* check the session state */
3236 SessionState_T state;
3237 HRESULT rc = aSession->COMGETTER(State)(&state);
3238 if (FAILED(rc)) return rc;
3239
3240 if (state != SessionState_Unlocked)
3241 return setError(VBOX_E_INVALID_OBJECT_STATE,
3242 tr("The given session is busy"));
3243
3244 // get the client's IInternalSessionControl interface
3245 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3246 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3247 E_INVALIDARG);
3248
3249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 if (!mData->mRegistered)
3252 return setError(E_UNEXPECTED,
3253 tr("The machine '%s' is not registered"),
3254 mUserData->s.strName.c_str());
3255
3256 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3257
3258 SessionState_T oldState = mData->mSession.mState;
3259 /* Hack: in case the session is closing and there is a progress object
3260 * which allows waiting for the session to be closed, take the opportunity
3261 * and do a limited wait (max. 1 second). This helps a lot when the system
3262 * is busy and thus session closing can take a little while. */
3263 if ( mData->mSession.mState == SessionState_Unlocking
3264 && mData->mSession.mProgress)
3265 {
3266 alock.release();
3267 mData->mSession.mProgress->WaitForCompletion(1000);
3268 alock.acquire();
3269 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3270 }
3271
3272 // try again now
3273 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3274 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3275 )
3276 {
3277 // OK, share the session... we are now dealing with three processes:
3278 // 1) VBoxSVC (where this code runs);
3279 // 2) process C: the caller's client process (who wants a shared session);
3280 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3281
3282 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3283 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3284 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3285 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3286 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3287
3288 /*
3289 * Release the lock before calling the client process. It's safe here
3290 * since the only thing to do after we get the lock again is to add
3291 * the remote control to the list (which doesn't directly influence
3292 * anything).
3293 */
3294 alock.release();
3295
3296 // get the console of the session holding the write lock (this is a remote call)
3297 ComPtr<IConsole> pConsoleW;
3298 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3299 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3300 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3301 if (FAILED(rc))
3302 // the failure may occur w/o any error info (from RPC), so provide one
3303 return setError(VBOX_E_VM_ERROR,
3304 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3305
3306 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3307
3308 // share the session machine and W's console with the caller's session
3309 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3310 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3311 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3312
3313 if (FAILED(rc))
3314 // the failure may occur w/o any error info (from RPC), so provide one
3315 return setError(VBOX_E_VM_ERROR,
3316 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3317 alock.acquire();
3318
3319 // need to revalidate the state after acquiring the lock again
3320 if (mData->mSession.mState != SessionState_Locked)
3321 {
3322 pSessionControl->Uninitialize();
3323 return setError(VBOX_E_INVALID_SESSION_STATE,
3324 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3325 mUserData->s.strName.c_str());
3326 }
3327
3328 // add the caller's session to the list
3329 mData->mSession.mRemoteControls.push_back(pSessionControl);
3330 }
3331 else if ( mData->mSession.mState == SessionState_Locked
3332 || mData->mSession.mState == SessionState_Unlocking
3333 )
3334 {
3335 // sharing not permitted, or machine still unlocking:
3336 return setError(VBOX_E_INVALID_OBJECT_STATE,
3337 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3338 mUserData->s.strName.c_str());
3339 }
3340 else
3341 {
3342 // machine is not locked: then write-lock the machine (create the session machine)
3343
3344 // must not be busy
3345 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3346
3347 // get the caller's session PID
3348 RTPROCESS pid = NIL_RTPROCESS;
3349 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3350 pSessionControl->GetPID((ULONG*)&pid);
3351 Assert(pid != NIL_RTPROCESS);
3352
3353 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3354
3355 if (fLaunchingVMProcess)
3356 {
3357 // this machine is awaiting for a spawning session to be opened:
3358 // then the calling process must be the one that got started by
3359 // LaunchVMProcess()
3360
3361 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3362 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3363
3364 if (mData->mSession.mPID != pid)
3365 return setError(E_ACCESSDENIED,
3366 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3367 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3368 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3369 }
3370
3371 // create the mutable SessionMachine from the current machine
3372 ComObjPtr<SessionMachine> sessionMachine;
3373 sessionMachine.createObject();
3374 rc = sessionMachine->init(this);
3375 AssertComRC(rc);
3376
3377 /* NOTE: doing return from this function after this point but
3378 * before the end is forbidden since it may call SessionMachine::uninit()
3379 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3380 * lock while still holding the Machine lock in alock so that a deadlock
3381 * is possible due to the wrong lock order. */
3382
3383 if (SUCCEEDED(rc))
3384 {
3385 /*
3386 * Set the session state to Spawning to protect against subsequent
3387 * attempts to open a session and to unregister the machine after
3388 * we release the lock.
3389 */
3390 SessionState_T origState = mData->mSession.mState;
3391 mData->mSession.mState = SessionState_Spawning;
3392
3393 /*
3394 * Release the lock before calling the client process -- it will call
3395 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3396 * because the state is Spawning, so that LaunchVMProcess() and
3397 * LockMachine() calls will fail. This method, called before we
3398 * acquire the lock again, will fail because of the wrong PID.
3399 *
3400 * Note that mData->mSession.mRemoteControls accessed outside
3401 * the lock may not be modified when state is Spawning, so it's safe.
3402 */
3403 alock.release();
3404
3405 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3406 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3407 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3408
3409 /* The failure may occur w/o any error info (from RPC), so provide one */
3410 if (FAILED(rc))
3411 setError(VBOX_E_VM_ERROR,
3412 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3413
3414 if ( SUCCEEDED(rc)
3415 && fLaunchingVMProcess
3416 )
3417 {
3418 /* complete the remote session initialization */
3419
3420 /* get the console from the direct session */
3421 ComPtr<IConsole> console;
3422 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3423 ComAssertComRC(rc);
3424
3425 if (SUCCEEDED(rc) && !console)
3426 {
3427 ComAssert(!!console);
3428 rc = E_FAIL;
3429 }
3430
3431 /* assign machine & console to the remote session */
3432 if (SUCCEEDED(rc))
3433 {
3434 /*
3435 * after LaunchVMProcess(), the first and the only
3436 * entry in remoteControls is that remote session
3437 */
3438 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3439 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3440 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3441
3442 /* The failure may occur w/o any error info (from RPC), so provide one */
3443 if (FAILED(rc))
3444 setError(VBOX_E_VM_ERROR,
3445 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3446 }
3447
3448 if (FAILED(rc))
3449 pSessionControl->Uninitialize();
3450 }
3451
3452 /* acquire the lock again */
3453 alock.acquire();
3454
3455 /* Restore the session state */
3456 mData->mSession.mState = origState;
3457 }
3458
3459 // finalize spawning anyway (this is why we don't return on errors above)
3460 if (fLaunchingVMProcess)
3461 {
3462 /* Note that the progress object is finalized later */
3463 /** @todo Consider checking mData->mSession.mProgress for cancellation
3464 * around here. */
3465
3466 /* We don't reset mSession.mPID here because it is necessary for
3467 * SessionMachine::uninit() to reap the child process later. */
3468
3469 if (FAILED(rc))
3470 {
3471 /* Close the remote session, remove the remote control from the list
3472 * and reset session state to Closed (@note keep the code in sync
3473 * with the relevant part in openSession()). */
3474
3475 Assert(mData->mSession.mRemoteControls.size() == 1);
3476 if (mData->mSession.mRemoteControls.size() == 1)
3477 {
3478 ErrorInfoKeeper eik;
3479 mData->mSession.mRemoteControls.front()->Uninitialize();
3480 }
3481
3482 mData->mSession.mRemoteControls.clear();
3483 mData->mSession.mState = SessionState_Unlocked;
3484 }
3485 }
3486 else
3487 {
3488 /* memorize PID of the directly opened session */
3489 if (SUCCEEDED(rc))
3490 mData->mSession.mPID = pid;
3491 }
3492
3493 if (SUCCEEDED(rc))
3494 {
3495 /* memorize the direct session control and cache IUnknown for it */
3496 mData->mSession.mDirectControl = pSessionControl;
3497 mData->mSession.mState = SessionState_Locked;
3498 /* associate the SessionMachine with this Machine */
3499 mData->mSession.mMachine = sessionMachine;
3500
3501 /* request an IUnknown pointer early from the remote party for later
3502 * identity checks (it will be internally cached within mDirectControl
3503 * at least on XPCOM) */
3504 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3505 NOREF(unk);
3506 }
3507
3508 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3509 * would break the lock order */
3510 alock.release();
3511
3512 /* uninitialize the created session machine on failure */
3513 if (FAILED(rc))
3514 sessionMachine->uninit();
3515
3516 }
3517
3518 if (SUCCEEDED(rc))
3519 {
3520 /*
3521 * tell the client watcher thread to update the set of
3522 * machines that have open sessions
3523 */
3524 mParent->updateClientWatcher();
3525
3526 if (oldState != SessionState_Locked)
3527 /* fire an event */
3528 mParent->onSessionStateChange(getId(), SessionState_Locked);
3529 }
3530
3531 return rc;
3532}
3533
3534/**
3535 * @note Locks objects!
3536 */
3537STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3538 IN_BSTR aType,
3539 IN_BSTR aEnvironment,
3540 IProgress **aProgress)
3541{
3542 CheckComArgStrNotEmptyOrNull(aType);
3543 Utf8Str strType(aType);
3544 Utf8Str strEnvironment(aEnvironment);
3545 /* "emergencystop" doesn't need the session, so skip the checks/interface
3546 * retrieval. This code doesn't quite fit in here, but introducing a
3547 * special API method would be even more effort, and would require explicit
3548 * support by every API client. It's better to hide the feature a bit. */
3549 if (strType != "emergencystop")
3550 CheckComArgNotNull(aSession);
3551 CheckComArgOutPointerValid(aProgress);
3552
3553 AutoCaller autoCaller(this);
3554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3555
3556 ComPtr<IInternalSessionControl> control;
3557 HRESULT rc = S_OK;
3558
3559 if (strType != "emergencystop")
3560 {
3561 /* check the session state */
3562 SessionState_T state;
3563 rc = aSession->COMGETTER(State)(&state);
3564 if (FAILED(rc))
3565 return rc;
3566
3567 if (state != SessionState_Unlocked)
3568 return setError(VBOX_E_INVALID_OBJECT_STATE,
3569 tr("The given session is busy"));
3570
3571 /* get the IInternalSessionControl interface */
3572 control = aSession;
3573 ComAssertMsgRet(!control.isNull(),
3574 ("No IInternalSessionControl interface"),
3575 E_INVALIDARG);
3576 }
3577
3578 /* get the teleporter enable state for the progress object init. */
3579 BOOL fTeleporterEnabled;
3580 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3581 if (FAILED(rc))
3582 return rc;
3583
3584 /* create a progress object */
3585 if (strType != "emergencystop")
3586 {
3587 ComObjPtr<ProgressProxy> progress;
3588 progress.createObject();
3589 rc = progress->init(mParent,
3590 static_cast<IMachine*>(this),
3591 Bstr(tr("Starting VM")).raw(),
3592 TRUE /* aCancelable */,
3593 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3594 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3595 2 /* uFirstOperationWeight */,
3596 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3597
3598 if (SUCCEEDED(rc))
3599 {
3600 rc = launchVMProcess(control, strType, strEnvironment, progress);
3601 if (SUCCEEDED(rc))
3602 {
3603 progress.queryInterfaceTo(aProgress);
3604
3605 /* signal the client watcher thread */
3606 mParent->updateClientWatcher();
3607
3608 /* fire an event */
3609 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3610 }
3611 }
3612 }
3613 else
3614 {
3615 /* no progress object - either instant success or failure */
3616 *aProgress = NULL;
3617
3618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3619
3620 if (mData->mSession.mState != SessionState_Locked)
3621 return setError(VBOX_E_INVALID_OBJECT_STATE,
3622 tr("The machine '%s' is not locked by a session"),
3623 mUserData->s.strName.c_str());
3624
3625 /* must have a VM process associated - do not kill normal API clients
3626 * with an open session */
3627 if (!Global::IsOnline(mData->mMachineState))
3628 return setError(VBOX_E_INVALID_OBJECT_STATE,
3629 tr("The machine '%s' does not have a VM process"),
3630 mUserData->s.strName.c_str());
3631
3632 /* forcibly terminate the VM process */
3633 if (mData->mSession.mPID != NIL_RTPROCESS)
3634 RTProcTerminate(mData->mSession.mPID);
3635
3636 /* signal the client watcher thread, as most likely the client has
3637 * been terminated */
3638 mParent->updateClientWatcher();
3639 }
3640
3641 return rc;
3642}
3643
3644STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3645{
3646 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3647 return setError(E_INVALIDARG,
3648 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3649 aPosition, SchemaDefs::MaxBootPosition);
3650
3651 if (aDevice == DeviceType_USB)
3652 return setError(E_NOTIMPL,
3653 tr("Booting from USB device is currently not supported"));
3654
3655 AutoCaller autoCaller(this);
3656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3657
3658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3659
3660 HRESULT rc = checkStateDependency(MutableStateDep);
3661 if (FAILED(rc)) return rc;
3662
3663 setModified(IsModified_MachineData);
3664 mHWData.backup();
3665 mHWData->mBootOrder[aPosition - 1] = aDevice;
3666
3667 return S_OK;
3668}
3669
3670STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3671{
3672 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3673 return setError(E_INVALIDARG,
3674 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3675 aPosition, SchemaDefs::MaxBootPosition);
3676
3677 AutoCaller autoCaller(this);
3678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3679
3680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3681
3682 *aDevice = mHWData->mBootOrder[aPosition - 1];
3683
3684 return S_OK;
3685}
3686
3687STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3688 LONG aControllerPort,
3689 LONG aDevice,
3690 DeviceType_T aType,
3691 IMedium *aMedium)
3692{
3693 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3694 aControllerName, aControllerPort, aDevice, aType, aMedium));
3695
3696 CheckComArgStrNotEmptyOrNull(aControllerName);
3697
3698 AutoCaller autoCaller(this);
3699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3700
3701 // request the host lock first, since might be calling Host methods for getting host drives;
3702 // next, protect the media tree all the while we're in here, as well as our member variables
3703 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3704 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3705
3706 HRESULT rc = checkStateDependency(MutableStateDep);
3707 if (FAILED(rc)) return rc;
3708
3709 /// @todo NEWMEDIA implicit machine registration
3710 if (!mData->mRegistered)
3711 return setError(VBOX_E_INVALID_OBJECT_STATE,
3712 tr("Cannot attach storage devices to an unregistered machine"));
3713
3714 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3715
3716 /* Check for an existing controller. */
3717 ComObjPtr<StorageController> ctl;
3718 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3719 if (FAILED(rc)) return rc;
3720
3721 StorageControllerType_T ctrlType;
3722 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3723 if (FAILED(rc))
3724 return setError(E_FAIL,
3725 tr("Could not get type of controller '%ls'"),
3726 aControllerName);
3727
3728 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3729 bool fHotplug = false;
3730 if (Global::IsOnlineOrTransient(mData->mMachineState))
3731 fHotplug = true;
3732
3733 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3734 return setError(VBOX_E_INVALID_VM_STATE,
3735 tr("Controller '%ls' does not support hotplugging"),
3736 aControllerName);
3737
3738 // check that the port and device are not out of range
3739 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3740 if (FAILED(rc)) return rc;
3741
3742 /* check if the device slot is already busy */
3743 MediumAttachment *pAttachTemp;
3744 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3745 aControllerName,
3746 aControllerPort,
3747 aDevice)))
3748 {
3749 Medium *pMedium = pAttachTemp->getMedium();
3750 if (pMedium)
3751 {
3752 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3753 return setError(VBOX_E_OBJECT_IN_USE,
3754 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3755 pMedium->getLocationFull().c_str(),
3756 aControllerPort,
3757 aDevice,
3758 aControllerName);
3759 }
3760 else
3761 return setError(VBOX_E_OBJECT_IN_USE,
3762 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3763 aControllerPort, aDevice, aControllerName);
3764 }
3765
3766 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3767 if (aMedium && medium.isNull())
3768 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3769
3770 AutoCaller mediumCaller(medium);
3771 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3772
3773 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3774
3775 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3776 && !medium.isNull()
3777 )
3778 return setError(VBOX_E_OBJECT_IN_USE,
3779 tr("Medium '%s' is already attached to this virtual machine"),
3780 medium->getLocationFull().c_str());
3781
3782 if (!medium.isNull())
3783 {
3784 MediumType_T mtype = medium->getType();
3785 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3786 // For DVDs it's not written to the config file, so needs no global config
3787 // version bump. For floppies it's a new attribute "type", which is ignored
3788 // by older VirtualBox version, so needs no global config version bump either.
3789 // For hard disks this type is not accepted.
3790 if (mtype == MediumType_MultiAttach)
3791 {
3792 // This type is new with VirtualBox 4.0 and therefore requires settings
3793 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3794 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3795 // two reasons: The medium type is a property of the media registry tree, which
3796 // can reside in the global config file (for pre-4.0 media); we would therefore
3797 // possibly need to bump the global config version. We don't want to do that though
3798 // because that might make downgrading to pre-4.0 impossible.
3799 // As a result, we can only use these two new types if the medium is NOT in the
3800 // global registry:
3801 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3802 if ( medium->isInRegistry(uuidGlobalRegistry)
3803 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3804 )
3805 return setError(VBOX_E_INVALID_OBJECT_STATE,
3806 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3807 "to machines that were created with VirtualBox 4.0 or later"),
3808 medium->getLocationFull().c_str());
3809 }
3810 }
3811
3812 bool fIndirect = false;
3813 if (!medium.isNull())
3814 fIndirect = medium->isReadOnly();
3815 bool associate = true;
3816
3817 do
3818 {
3819 if ( aType == DeviceType_HardDisk
3820 && mMediaData.isBackedUp())
3821 {
3822 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3823
3824 /* check if the medium was attached to the VM before we started
3825 * changing attachments in which case the attachment just needs to
3826 * be restored */
3827 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3828 {
3829 AssertReturn(!fIndirect, E_FAIL);
3830
3831 /* see if it's the same bus/channel/device */
3832 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3833 {
3834 /* the simplest case: restore the whole attachment
3835 * and return, nothing else to do */
3836 mMediaData->mAttachments.push_back(pAttachTemp);
3837 return S_OK;
3838 }
3839
3840 /* bus/channel/device differ; we need a new attachment object,
3841 * but don't try to associate it again */
3842 associate = false;
3843 break;
3844 }
3845 }
3846
3847 /* go further only if the attachment is to be indirect */
3848 if (!fIndirect)
3849 break;
3850
3851 /* perform the so called smart attachment logic for indirect
3852 * attachments. Note that smart attachment is only applicable to base
3853 * hard disks. */
3854
3855 if (medium->getParent().isNull())
3856 {
3857 /* first, investigate the backup copy of the current hard disk
3858 * attachments to make it possible to re-attach existing diffs to
3859 * another device slot w/o losing their contents */
3860 if (mMediaData.isBackedUp())
3861 {
3862 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3863
3864 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3865 uint32_t foundLevel = 0;
3866
3867 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3868 it != oldAtts.end();
3869 ++it)
3870 {
3871 uint32_t level = 0;
3872 MediumAttachment *pAttach = *it;
3873 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3874 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3875 if (pMedium.isNull())
3876 continue;
3877
3878 if (pMedium->getBase(&level) == medium)
3879 {
3880 /* skip the hard disk if its currently attached (we
3881 * cannot attach the same hard disk twice) */
3882 if (findAttachment(mMediaData->mAttachments,
3883 pMedium))
3884 continue;
3885
3886 /* matched device, channel and bus (i.e. attached to the
3887 * same place) will win and immediately stop the search;
3888 * otherwise the attachment that has the youngest
3889 * descendant of medium will be used
3890 */
3891 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3892 {
3893 /* the simplest case: restore the whole attachment
3894 * and return, nothing else to do */
3895 mMediaData->mAttachments.push_back(*it);
3896 return S_OK;
3897 }
3898 else if ( foundIt == oldAtts.end()
3899 || level > foundLevel /* prefer younger */
3900 )
3901 {
3902 foundIt = it;
3903 foundLevel = level;
3904 }
3905 }
3906 }
3907
3908 if (foundIt != oldAtts.end())
3909 {
3910 /* use the previously attached hard disk */
3911 medium = (*foundIt)->getMedium();
3912 mediumCaller.attach(medium);
3913 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3914 mediumLock.attach(medium);
3915 /* not implicit, doesn't require association with this VM */
3916 fIndirect = false;
3917 associate = false;
3918 /* go right to the MediumAttachment creation */
3919 break;
3920 }
3921 }
3922
3923 /* must give up the medium lock and medium tree lock as below we
3924 * go over snapshots, which needs a lock with higher lock order. */
3925 mediumLock.release();
3926 treeLock.release();
3927
3928 /* then, search through snapshots for the best diff in the given
3929 * hard disk's chain to base the new diff on */
3930
3931 ComObjPtr<Medium> base;
3932 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3933 while (snap)
3934 {
3935 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3936
3937 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3938
3939 MediumAttachment *pAttachFound = NULL;
3940 uint32_t foundLevel = 0;
3941
3942 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3943 it != snapAtts.end();
3944 ++it)
3945 {
3946 MediumAttachment *pAttach = *it;
3947 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3948 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3949 if (pMedium.isNull())
3950 continue;
3951
3952 uint32_t level = 0;
3953 if (pMedium->getBase(&level) == medium)
3954 {
3955 /* matched device, channel and bus (i.e. attached to the
3956 * same place) will win and immediately stop the search;
3957 * otherwise the attachment that has the youngest
3958 * descendant of medium will be used
3959 */
3960 if ( pAttach->getDevice() == aDevice
3961 && pAttach->getPort() == aControllerPort
3962 && pAttach->getControllerName() == aControllerName
3963 )
3964 {
3965 pAttachFound = pAttach;
3966 break;
3967 }
3968 else if ( !pAttachFound
3969 || level > foundLevel /* prefer younger */
3970 )
3971 {
3972 pAttachFound = pAttach;
3973 foundLevel = level;
3974 }
3975 }
3976 }
3977
3978 if (pAttachFound)
3979 {
3980 base = pAttachFound->getMedium();
3981 break;
3982 }
3983
3984 snap = snap->getParent();
3985 }
3986
3987 /* re-lock medium tree and the medium, as we need it below */
3988 treeLock.acquire();
3989 mediumLock.acquire();
3990
3991 /* found a suitable diff, use it as a base */
3992 if (!base.isNull())
3993 {
3994 medium = base;
3995 mediumCaller.attach(medium);
3996 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3997 mediumLock.attach(medium);
3998 }
3999 }
4000
4001 Utf8Str strFullSnapshotFolder;
4002 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4003
4004 ComObjPtr<Medium> diff;
4005 diff.createObject();
4006 // store this diff in the same registry as the parent
4007 Guid uuidRegistryParent;
4008 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4009 {
4010 // parent image has no registry: this can happen if we're attaching a new immutable
4011 // image that has not yet been attached (medium then points to the base and we're
4012 // creating the diff image for the immutable, and the parent is not yet registered);
4013 // put the parent in the machine registry then
4014 mediumLock.release();
4015 treeLock.release();
4016 alock.release();
4017 addMediumToRegistry(medium);
4018 alock.acquire();
4019 treeLock.acquire();
4020 mediumLock.acquire();
4021 medium->getFirstRegistryMachineId(uuidRegistryParent);
4022 }
4023 rc = diff->init(mParent,
4024 medium->getPreferredDiffFormat(),
4025 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4026 uuidRegistryParent);
4027 if (FAILED(rc)) return rc;
4028
4029 /* Apply the normal locking logic to the entire chain. */
4030 MediumLockList *pMediumLockList(new MediumLockList());
4031 mediumLock.release();
4032 treeLock.release();
4033 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4034 true /* fMediumLockWrite */,
4035 medium,
4036 *pMediumLockList);
4037 treeLock.acquire();
4038 mediumLock.acquire();
4039 if (SUCCEEDED(rc))
4040 {
4041 mediumLock.release();
4042 treeLock.release();
4043 rc = pMediumLockList->Lock();
4044 treeLock.acquire();
4045 mediumLock.acquire();
4046 if (FAILED(rc))
4047 setError(rc,
4048 tr("Could not lock medium when creating diff '%s'"),
4049 diff->getLocationFull().c_str());
4050 else
4051 {
4052 /* will release the lock before the potentially lengthy
4053 * operation, so protect with the special state */
4054 MachineState_T oldState = mData->mMachineState;
4055 setMachineState(MachineState_SettingUp);
4056
4057 mediumLock.release();
4058 treeLock.release();
4059 alock.release();
4060
4061 rc = medium->createDiffStorage(diff,
4062 MediumVariant_Standard,
4063 pMediumLockList,
4064 NULL /* aProgress */,
4065 true /* aWait */);
4066
4067 alock.acquire();
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070
4071 setMachineState(oldState);
4072 }
4073 }
4074
4075 /* Unlock the media and free the associated memory. */
4076 delete pMediumLockList;
4077
4078 if (FAILED(rc)) return rc;
4079
4080 /* use the created diff for the actual attachment */
4081 medium = diff;
4082 mediumCaller.attach(medium);
4083 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4084 mediumLock.attach(medium);
4085 }
4086 while (0);
4087
4088 ComObjPtr<MediumAttachment> attachment;
4089 attachment.createObject();
4090 rc = attachment->init(this,
4091 medium,
4092 aControllerName,
4093 aControllerPort,
4094 aDevice,
4095 aType,
4096 fIndirect,
4097 false /* fPassthrough */,
4098 false /* fTempEject */,
4099 false /* fNonRotational */,
4100 false /* fDiscard */,
4101 Utf8Str::Empty);
4102 if (FAILED(rc)) return rc;
4103
4104 if (associate && !medium.isNull())
4105 {
4106 // as the last step, associate the medium to the VM
4107 rc = medium->addBackReference(mData->mUuid);
4108 // here we can fail because of Deleting, or being in process of creating a Diff
4109 if (FAILED(rc)) return rc;
4110
4111 mediumLock.release();
4112 treeLock.release();
4113 alock.release();
4114 addMediumToRegistry(medium);
4115 alock.acquire();
4116 treeLock.acquire();
4117 mediumLock.acquire();
4118 }
4119
4120 /* success: finally remember the attachment */
4121 setModified(IsModified_Storage);
4122 mMediaData.backup();
4123 mMediaData->mAttachments.push_back(attachment);
4124
4125 mediumLock.release();
4126 treeLock.release();
4127 alock.release();
4128
4129 if (fHotplug)
4130 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4131
4132 mParent->saveModifiedRegistries();
4133
4134 return rc;
4135}
4136
4137STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4138 LONG aDevice)
4139{
4140 CheckComArgStrNotEmptyOrNull(aControllerName);
4141
4142 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4143 aControllerName, aControllerPort, aDevice));
4144
4145 AutoCaller autoCaller(this);
4146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4147
4148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4149
4150 HRESULT rc = checkStateDependency(MutableStateDep);
4151 if (FAILED(rc)) return rc;
4152
4153 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4154
4155 /* Check for an existing controller. */
4156 ComObjPtr<StorageController> ctl;
4157 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4158 if (FAILED(rc)) return rc;
4159
4160 StorageControllerType_T ctrlType;
4161 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4162 if (FAILED(rc))
4163 return setError(E_FAIL,
4164 tr("Could not get type of controller '%ls'"),
4165 aControllerName);
4166
4167 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4168 bool fHotplug = false;
4169 if (Global::IsOnlineOrTransient(mData->mMachineState))
4170 fHotplug = true;
4171
4172 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4173 return setError(VBOX_E_INVALID_VM_STATE,
4174 tr("Controller '%ls' does not support hotplugging"),
4175 aControllerName);
4176
4177 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4178 aControllerName,
4179 aControllerPort,
4180 aDevice);
4181 if (!pAttach)
4182 return setError(VBOX_E_OBJECT_NOT_FOUND,
4183 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4184 aDevice, aControllerPort, aControllerName);
4185
4186 /*
4187 * The VM has to detach the device before we delete any implicit diffs.
4188 * If this fails we can roll back without loosing data.
4189 */
4190 if (fHotplug)
4191 {
4192 alock.release();
4193 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4194 alock.acquire();
4195 }
4196 if (FAILED(rc)) return rc;
4197
4198 /* If we are here everything went well and we can delete the implicit now. */
4199 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4200
4201 alock.release();
4202
4203 mParent->saveModifiedRegistries();
4204
4205 return rc;
4206}
4207
4208STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4209 LONG aDevice, BOOL aPassthrough)
4210{
4211 CheckComArgStrNotEmptyOrNull(aControllerName);
4212
4213 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4214 aControllerName, aControllerPort, aDevice, aPassthrough));
4215
4216 AutoCaller autoCaller(this);
4217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4218
4219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4220
4221 HRESULT rc = checkStateDependency(MutableStateDep);
4222 if (FAILED(rc)) return rc;
4223
4224 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4225
4226 if (Global::IsOnlineOrTransient(mData->mMachineState))
4227 return setError(VBOX_E_INVALID_VM_STATE,
4228 tr("Invalid machine state: %s"),
4229 Global::stringifyMachineState(mData->mMachineState));
4230
4231 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4232 aControllerName,
4233 aControllerPort,
4234 aDevice);
4235 if (!pAttach)
4236 return setError(VBOX_E_OBJECT_NOT_FOUND,
4237 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4238 aDevice, aControllerPort, aControllerName);
4239
4240
4241 setModified(IsModified_Storage);
4242 mMediaData.backup();
4243
4244 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4245
4246 if (pAttach->getType() != DeviceType_DVD)
4247 return setError(E_INVALIDARG,
4248 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4249 aDevice, aControllerPort, aControllerName);
4250 pAttach->updatePassthrough(!!aPassthrough);
4251
4252 return S_OK;
4253}
4254
4255STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4256 LONG aDevice, BOOL aTemporaryEject)
4257{
4258 CheckComArgStrNotEmptyOrNull(aControllerName);
4259
4260 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4261 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4262
4263 AutoCaller autoCaller(this);
4264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4265
4266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4267
4268 HRESULT rc = checkStateDependency(MutableStateDep);
4269 if (FAILED(rc)) return rc;
4270
4271 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4272 aControllerName,
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4278 aDevice, aControllerPort, aControllerName);
4279
4280
4281 setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4285
4286 if (pAttach->getType() != DeviceType_DVD)
4287 return setError(E_INVALIDARG,
4288 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4289 aDevice, aControllerPort, aControllerName);
4290 pAttach->updateTempEject(!!aTemporaryEject);
4291
4292 return S_OK;
4293}
4294
4295STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4296 LONG aDevice, BOOL aNonRotational)
4297{
4298 CheckComArgStrNotEmptyOrNull(aControllerName);
4299
4300 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4301 aControllerName, aControllerPort, aDevice, aNonRotational));
4302
4303 AutoCaller autoCaller(this);
4304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4305
4306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4307
4308 HRESULT rc = checkStateDependency(MutableStateDep);
4309 if (FAILED(rc)) return rc;
4310
4311 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4312
4313 if (Global::IsOnlineOrTransient(mData->mMachineState))
4314 return setError(VBOX_E_INVALID_VM_STATE,
4315 tr("Invalid machine state: %s"),
4316 Global::stringifyMachineState(mData->mMachineState));
4317
4318 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4319 aControllerName,
4320 aControllerPort,
4321 aDevice);
4322 if (!pAttach)
4323 return setError(VBOX_E_OBJECT_NOT_FOUND,
4324 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4325 aDevice, aControllerPort, aControllerName);
4326
4327
4328 setModified(IsModified_Storage);
4329 mMediaData.backup();
4330
4331 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4332
4333 if (pAttach->getType() != DeviceType_HardDisk)
4334 return setError(E_INVALIDARG,
4335 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4336 aDevice, aControllerPort, aControllerName);
4337 pAttach->updateNonRotational(!!aNonRotational);
4338
4339 return S_OK;
4340}
4341
4342STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4343 LONG aDevice, BOOL aDiscard)
4344{
4345 CheckComArgStrNotEmptyOrNull(aControllerName);
4346
4347 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4348 aControllerName, aControllerPort, aDevice, aDiscard));
4349
4350 AutoCaller autoCaller(this);
4351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4352
4353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4354
4355 HRESULT rc = checkStateDependency(MutableStateDep);
4356 if (FAILED(rc)) return rc;
4357
4358 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4359
4360 if (Global::IsOnlineOrTransient(mData->mMachineState))
4361 return setError(VBOX_E_INVALID_VM_STATE,
4362 tr("Invalid machine state: %s"),
4363 Global::stringifyMachineState(mData->mMachineState));
4364
4365 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4366 aControllerName,
4367 aControllerPort,
4368 aDevice);
4369 if (!pAttach)
4370 return setError(VBOX_E_OBJECT_NOT_FOUND,
4371 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4372 aDevice, aControllerPort, aControllerName);
4373
4374
4375 setModified(IsModified_Storage);
4376 mMediaData.backup();
4377
4378 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4379
4380 if (pAttach->getType() != DeviceType_HardDisk)
4381 return setError(E_INVALIDARG,
4382 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4383 aDevice, aControllerPort, aControllerName);
4384 pAttach->updateDiscard(!!aDiscard);
4385
4386 return S_OK;
4387}
4388
4389STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4390 LONG aDevice)
4391{
4392 int rc = S_OK;
4393 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4394 aControllerName, aControllerPort, aDevice));
4395
4396 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4397
4398 return rc;
4399}
4400
4401STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4402 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4403{
4404 CheckComArgStrNotEmptyOrNull(aControllerName);
4405
4406 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4407 aControllerName, aControllerPort, aDevice));
4408
4409 AutoCaller autoCaller(this);
4410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4411
4412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4413
4414 HRESULT rc = checkStateDependency(MutableStateDep);
4415 if (FAILED(rc)) return rc;
4416
4417 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4418
4419 if (Global::IsOnlineOrTransient(mData->mMachineState))
4420 return setError(VBOX_E_INVALID_VM_STATE,
4421 tr("Invalid machine state: %s"),
4422 Global::stringifyMachineState(mData->mMachineState));
4423
4424 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4425 aControllerName,
4426 aControllerPort,
4427 aDevice);
4428 if (!pAttach)
4429 return setError(VBOX_E_OBJECT_NOT_FOUND,
4430 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4431 aDevice, aControllerPort, aControllerName);
4432
4433
4434 setModified(IsModified_Storage);
4435 mMediaData.backup();
4436
4437 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4438 if (aBandwidthGroup && group.isNull())
4439 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4440
4441 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4442
4443 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4444 if (strBandwidthGroupOld.isNotEmpty())
4445 {
4446 /* Get the bandwidth group object and release it - this must not fail. */
4447 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4448 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4449 Assert(SUCCEEDED(rc));
4450
4451 pBandwidthGroupOld->release();
4452 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4453 }
4454
4455 if (!group.isNull())
4456 {
4457 group->reference();
4458 pAttach->updateBandwidthGroup(group->getName());
4459 }
4460
4461 return S_OK;
4462}
4463
4464STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4465 LONG aControllerPort,
4466 LONG aDevice,
4467 DeviceType_T aType)
4468{
4469 HRESULT rc = S_OK;
4470
4471 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4472 aControllerName, aControllerPort, aDevice, aType));
4473
4474 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4475
4476 return rc;
4477}
4478
4479
4480
4481STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 BOOL aForce)
4485{
4486 int rc = S_OK;
4487 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4488 aControllerName, aControllerPort, aForce));
4489
4490 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4491
4492 return rc;
4493}
4494
4495STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4496 LONG aControllerPort,
4497 LONG aDevice,
4498 IMedium *aMedium,
4499 BOOL aForce)
4500{
4501 int rc = S_OK;
4502 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4503 aControllerName, aControllerPort, aDevice, aForce));
4504
4505 CheckComArgStrNotEmptyOrNull(aControllerName);
4506
4507 AutoCaller autoCaller(this);
4508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4509
4510 // request the host lock first, since might be calling Host methods for getting host drives;
4511 // next, protect the media tree all the while we're in here, as well as our member variables
4512 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4513 this->lockHandle(),
4514 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4515
4516 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4517 aControllerName,
4518 aControllerPort,
4519 aDevice);
4520 if (pAttach.isNull())
4521 return setError(VBOX_E_OBJECT_NOT_FOUND,
4522 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4523 aDevice, aControllerPort, aControllerName);
4524
4525 /* Remember previously mounted medium. The medium before taking the
4526 * backup is not necessarily the same thing. */
4527 ComObjPtr<Medium> oldmedium;
4528 oldmedium = pAttach->getMedium();
4529
4530 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4531 if (aMedium && pMedium.isNull())
4532 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4533
4534 AutoCaller mediumCaller(pMedium);
4535 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4536
4537 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4538 if (pMedium)
4539 {
4540 DeviceType_T mediumType = pAttach->getType();
4541 switch (mediumType)
4542 {
4543 case DeviceType_DVD:
4544 case DeviceType_Floppy:
4545 break;
4546
4547 default:
4548 return setError(VBOX_E_INVALID_OBJECT_STATE,
4549 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4550 aControllerPort,
4551 aDevice,
4552 aControllerName);
4553 }
4554 }
4555
4556 setModified(IsModified_Storage);
4557 mMediaData.backup();
4558
4559 {
4560 // The backup operation makes the pAttach reference point to the
4561 // old settings. Re-get the correct reference.
4562 pAttach = findAttachment(mMediaData->mAttachments,
4563 aControllerName,
4564 aControllerPort,
4565 aDevice);
4566 if (!oldmedium.isNull())
4567 oldmedium->removeBackReference(mData->mUuid);
4568 if (!pMedium.isNull())
4569 {
4570 pMedium->addBackReference(mData->mUuid);
4571
4572 mediumLock.release();
4573 multiLock.release();
4574 addMediumToRegistry(pMedium);
4575 multiLock.acquire();
4576 mediumLock.acquire();
4577 }
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580 pAttach->updateMedium(pMedium);
4581 }
4582
4583 setModified(IsModified_Storage);
4584
4585 mediumLock.release();
4586 multiLock.release();
4587 rc = onMediumChange(pAttach, aForce);
4588 multiLock.acquire();
4589 mediumLock.acquire();
4590
4591 /* On error roll back this change only. */
4592 if (FAILED(rc))
4593 {
4594 if (!pMedium.isNull())
4595 pMedium->removeBackReference(mData->mUuid);
4596 pAttach = findAttachment(mMediaData->mAttachments,
4597 aControllerName,
4598 aControllerPort,
4599 aDevice);
4600 /* If the attachment is gone in the meantime, bail out. */
4601 if (pAttach.isNull())
4602 return rc;
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604 if (!oldmedium.isNull())
4605 oldmedium->addBackReference(mData->mUuid);
4606 pAttach->updateMedium(oldmedium);
4607 }
4608
4609 mediumLock.release();
4610 multiLock.release();
4611
4612 mParent->saveModifiedRegistries();
4613
4614 return rc;
4615}
4616
4617STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4618 LONG aControllerPort,
4619 LONG aDevice,
4620 IMedium **aMedium)
4621{
4622 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4623 aControllerName, aControllerPort, aDevice));
4624
4625 CheckComArgStrNotEmptyOrNull(aControllerName);
4626 CheckComArgOutPointerValid(aMedium);
4627
4628 AutoCaller autoCaller(this);
4629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 *aMedium = NULL;
4634
4635 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4636 aControllerName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4642 aDevice, aControllerPort, aControllerName);
4643
4644 pAttach->getMedium().queryInterfaceTo(aMedium);
4645
4646 return S_OK;
4647}
4648
4649STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4650{
4651 CheckComArgOutPointerValid(port);
4652 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4653
4654 AutoCaller autoCaller(this);
4655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4656
4657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4658
4659 mSerialPorts[slot].queryInterfaceTo(port);
4660
4661 return S_OK;
4662}
4663
4664STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4665{
4666 CheckComArgOutPointerValid(port);
4667 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4668
4669 AutoCaller autoCaller(this);
4670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4671
4672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4673
4674 mParallelPorts[slot].queryInterfaceTo(port);
4675
4676 return S_OK;
4677}
4678
4679STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4680{
4681 CheckComArgOutPointerValid(adapter);
4682 /* Do not assert if slot is out of range, just return the advertised
4683 status. testdriver/vbox.py triggers this in logVmInfo. */
4684 if (slot >= mNetworkAdapters.size())
4685 return setError(E_INVALIDARG,
4686 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4687 slot, mNetworkAdapters.size());
4688
4689 AutoCaller autoCaller(this);
4690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4691
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4695
4696 return S_OK;
4697}
4698
4699STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4700{
4701 CheckComArgOutSafeArrayPointerValid(aKeys);
4702
4703 AutoCaller autoCaller(this);
4704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4705
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4709 int i = 0;
4710 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4711 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4712 ++it, ++i)
4713 {
4714 const Utf8Str &strKey = it->first;
4715 strKey.cloneTo(&saKeys[i]);
4716 }
4717 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4718
4719 return S_OK;
4720 }
4721
4722 /**
4723 * @note Locks this object for reading.
4724 */
4725STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4726 BSTR *aValue)
4727{
4728 CheckComArgStrNotEmptyOrNull(aKey);
4729 CheckComArgOutPointerValid(aValue);
4730
4731 AutoCaller autoCaller(this);
4732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4733
4734 /* start with nothing found */
4735 Bstr bstrResult("");
4736
4737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4738
4739 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4740 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4741 // found:
4742 bstrResult = it->second; // source is a Utf8Str
4743
4744 /* return the result to caller (may be empty) */
4745 bstrResult.cloneTo(aValue);
4746
4747 return S_OK;
4748}
4749
4750 /**
4751 * @note Locks mParent for writing + this object for writing.
4752 */
4753STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4754{
4755 CheckComArgStrNotEmptyOrNull(aKey);
4756
4757 AutoCaller autoCaller(this);
4758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4759
4760 Utf8Str strKey(aKey);
4761 Utf8Str strValue(aValue);
4762 Utf8Str strOldValue; // empty
4763
4764 // locking note: we only hold the read lock briefly to look up the old value,
4765 // then release it and call the onExtraCanChange callbacks. There is a small
4766 // chance of a race insofar as the callback might be called twice if two callers
4767 // change the same key at the same time, but that's a much better solution
4768 // than the deadlock we had here before. The actual changing of the extradata
4769 // is then performed under the write lock and race-free.
4770
4771 // look up the old value first; if nothing has changed then we need not do anything
4772 {
4773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4774 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4775 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4776 strOldValue = it->second;
4777 }
4778
4779 bool fChanged;
4780 if ((fChanged = (strOldValue != strValue)))
4781 {
4782 // ask for permission from all listeners outside the locks;
4783 // onExtraDataCanChange() only briefly requests the VirtualBox
4784 // lock to copy the list of callbacks to invoke
4785 Bstr error;
4786 Bstr bstrValue(aValue);
4787
4788 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4789 {
4790 const char *sep = error.isEmpty() ? "" : ": ";
4791 CBSTR err = error.raw();
4792 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4793 sep, err));
4794 return setError(E_ACCESSDENIED,
4795 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4796 aKey,
4797 bstrValue.raw(),
4798 sep,
4799 err);
4800 }
4801
4802 // data is changing and change not vetoed: then write it out under the lock
4803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 if (isSnapshotMachine())
4806 {
4807 HRESULT rc = checkStateDependency(MutableStateDep);
4808 if (FAILED(rc)) return rc;
4809 }
4810
4811 if (strValue.isEmpty())
4812 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4813 else
4814 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4815 // creates a new key if needed
4816
4817 bool fNeedsGlobalSaveSettings = false;
4818 saveSettings(&fNeedsGlobalSaveSettings);
4819
4820 if (fNeedsGlobalSaveSettings)
4821 {
4822 // save the global settings; for that we should hold only the VirtualBox lock
4823 alock.release();
4824 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4825 mParent->saveSettings();
4826 }
4827 }
4828
4829 // fire notification outside the lock
4830 if (fChanged)
4831 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4832
4833 return S_OK;
4834}
4835
4836STDMETHODIMP Machine::SaveSettings()
4837{
4838 AutoCaller autoCaller(this);
4839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4840
4841 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4842
4843 /* when there was auto-conversion, we want to save the file even if
4844 * the VM is saved */
4845 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4846 if (FAILED(rc)) return rc;
4847
4848 /* the settings file path may never be null */
4849 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4850
4851 /* save all VM data excluding snapshots */
4852 bool fNeedsGlobalSaveSettings = false;
4853 rc = saveSettings(&fNeedsGlobalSaveSettings);
4854 mlock.release();
4855
4856 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4857 {
4858 // save the global settings; for that we should hold only the VirtualBox lock
4859 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4860 rc = mParent->saveSettings();
4861 }
4862
4863 return rc;
4864}
4865
4866STDMETHODIMP Machine::DiscardSettings()
4867{
4868 AutoCaller autoCaller(this);
4869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4870
4871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4872
4873 HRESULT rc = checkStateDependency(MutableStateDep);
4874 if (FAILED(rc)) return rc;
4875
4876 /*
4877 * during this rollback, the session will be notified if data has
4878 * been actually changed
4879 */
4880 rollback(true /* aNotify */);
4881
4882 return S_OK;
4883}
4884
4885/** @note Locks objects! */
4886STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4887 ComSafeArrayOut(IMedium*, aMedia))
4888{
4889 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4890 AutoLimitedCaller autoCaller(this);
4891 AssertComRCReturnRC(autoCaller.rc());
4892
4893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 Guid id(getId());
4896
4897 if (mData->mSession.mState != SessionState_Unlocked)
4898 return setError(VBOX_E_INVALID_OBJECT_STATE,
4899 tr("Cannot unregister the machine '%s' while it is locked"),
4900 mUserData->s.strName.c_str());
4901
4902 // wait for state dependents to drop to zero
4903 ensureNoStateDependencies();
4904
4905 if (!mData->mAccessible)
4906 {
4907 // inaccessible maschines can only be unregistered; uninitialize ourselves
4908 // here because currently there may be no unregistered that are inaccessible
4909 // (this state combination is not supported). Note releasing the caller and
4910 // leaving the lock before calling uninit()
4911 alock.release();
4912 autoCaller.release();
4913
4914 uninit();
4915
4916 mParent->unregisterMachine(this, id);
4917 // calls VirtualBox::saveSettings()
4918
4919 return S_OK;
4920 }
4921
4922 HRESULT rc = S_OK;
4923
4924 // discard saved state
4925 if (mData->mMachineState == MachineState_Saved)
4926 {
4927 // add the saved state file to the list of files the caller should delete
4928 Assert(!mSSData->strStateFilePath.isEmpty());
4929 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4930
4931 mSSData->strStateFilePath.setNull();
4932
4933 // unconditionally set the machine state to powered off, we now
4934 // know no session has locked the machine
4935 mData->mMachineState = MachineState_PoweredOff;
4936 }
4937
4938 size_t cSnapshots = 0;
4939 if (mData->mFirstSnapshot)
4940 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4941 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4942 // fail now before we start detaching media
4943 return setError(VBOX_E_INVALID_OBJECT_STATE,
4944 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4945 mUserData->s.strName.c_str(), cSnapshots);
4946
4947 // This list collects the medium objects from all medium attachments
4948 // which we will detach from the machine and its snapshots, in a specific
4949 // order which allows for closing all media without getting "media in use"
4950 // errors, simply by going through the list from the front to the back:
4951 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4952 // and must be closed before the parent media from the snapshots, or closing the parents
4953 // will fail because they still have children);
4954 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4955 // the root ("first") snapshot of the machine.
4956 MediaList llMedia;
4957
4958 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4959 && mMediaData->mAttachments.size()
4960 )
4961 {
4962 // we have media attachments: detach them all and add the Medium objects to our list
4963 if (cleanupMode != CleanupMode_UnregisterOnly)
4964 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4965 else
4966 return setError(VBOX_E_INVALID_OBJECT_STATE,
4967 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4968 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4969 }
4970
4971 if (cSnapshots)
4972 {
4973 // autoCleanup must be true here, or we would have failed above
4974
4975 // add the media from the medium attachments of the snapshots to llMedia
4976 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4977 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4978 // into the children first
4979
4980 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4981 MachineState_T oldState = mData->mMachineState;
4982 mData->mMachineState = MachineState_DeletingSnapshot;
4983
4984 // make a copy of the first snapshot so the refcount does not drop to 0
4985 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4986 // because of the AutoCaller voodoo)
4987 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4988
4989 // GO!
4990 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4991
4992 mData->mMachineState = oldState;
4993 }
4994
4995 if (FAILED(rc))
4996 {
4997 rollbackMedia();
4998 return rc;
4999 }
5000
5001 // commit all the media changes made above
5002 commitMedia();
5003
5004 mData->mRegistered = false;
5005
5006 // machine lock no longer needed
5007 alock.release();
5008
5009 // return media to caller
5010 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5011 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5012
5013 mParent->unregisterMachine(this, id);
5014 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5015
5016 return S_OK;
5017}
5018
5019struct Machine::DeleteTask
5020{
5021 ComObjPtr<Machine> pMachine;
5022 RTCList<ComPtr<IMedium> > llMediums;
5023 StringsList llFilesToDelete;
5024 ComObjPtr<Progress> pProgress;
5025};
5026
5027STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5028{
5029 LogFlowFuncEnter();
5030
5031 AutoCaller autoCaller(this);
5032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5033
5034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 HRESULT rc = checkStateDependency(MutableStateDep);
5037 if (FAILED(rc)) return rc;
5038
5039 if (mData->mRegistered)
5040 return setError(VBOX_E_INVALID_VM_STATE,
5041 tr("Cannot delete settings of a registered machine"));
5042
5043 DeleteTask *pTask = new DeleteTask;
5044 pTask->pMachine = this;
5045 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5046
5047 // collect files to delete
5048 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5049
5050 for (size_t i = 0; i < sfaMedia.size(); ++i)
5051 {
5052 IMedium *pIMedium(sfaMedia[i]);
5053 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5054 if (pMedium.isNull())
5055 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5056 SafeArray<BSTR> ids;
5057 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5058 if (FAILED(rc)) return rc;
5059 /* At this point the medium should not have any back references
5060 * anymore. If it has it is attached to another VM and *must* not
5061 * deleted. */
5062 if (ids.size() < 1)
5063 pTask->llMediums.append(pMedium);
5064 }
5065 if (mData->pMachineConfigFile->fileExists())
5066 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5067
5068 pTask->pProgress.createObject();
5069 pTask->pProgress->init(getVirtualBox(),
5070 static_cast<IMachine*>(this) /* aInitiator */,
5071 Bstr(tr("Deleting files")).raw(),
5072 true /* fCancellable */,
5073 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5074 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5075
5076 int vrc = RTThreadCreate(NULL,
5077 Machine::deleteThread,
5078 (void*)pTask,
5079 0,
5080 RTTHREADTYPE_MAIN_WORKER,
5081 0,
5082 "MachineDelete");
5083
5084 pTask->pProgress.queryInterfaceTo(aProgress);
5085
5086 if (RT_FAILURE(vrc))
5087 {
5088 delete pTask;
5089 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5090 }
5091
5092 LogFlowFuncLeave();
5093
5094 return S_OK;
5095}
5096
5097/**
5098 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5099 * calls Machine::deleteTaskWorker() on the actual machine object.
5100 * @param Thread
5101 * @param pvUser
5102 * @return
5103 */
5104/*static*/
5105DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5106{
5107 LogFlowFuncEnter();
5108
5109 DeleteTask *pTask = (DeleteTask*)pvUser;
5110 Assert(pTask);
5111 Assert(pTask->pMachine);
5112 Assert(pTask->pProgress);
5113
5114 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5115 pTask->pProgress->notifyComplete(rc);
5116
5117 delete pTask;
5118
5119 LogFlowFuncLeave();
5120
5121 NOREF(Thread);
5122
5123 return VINF_SUCCESS;
5124}
5125
5126/**
5127 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5128 * @param task
5129 * @return
5130 */
5131HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5132{
5133 AutoCaller autoCaller(this);
5134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5135
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 HRESULT rc = S_OK;
5139
5140 try
5141 {
5142 ULONG uLogHistoryCount = 3;
5143 ComPtr<ISystemProperties> systemProperties;
5144 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5145 if (FAILED(rc)) throw rc;
5146
5147 if (!systemProperties.isNull())
5148 {
5149 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5150 if (FAILED(rc)) throw rc;
5151 }
5152
5153 MachineState_T oldState = mData->mMachineState;
5154 setMachineState(MachineState_SettingUp);
5155 alock.release();
5156 for (size_t i = 0; i < task.llMediums.size(); ++i)
5157 {
5158 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5159 {
5160 AutoCaller mac(pMedium);
5161 if (FAILED(mac.rc())) throw mac.rc();
5162 Utf8Str strLocation = pMedium->getLocationFull();
5163 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5164 if (FAILED(rc)) throw rc;
5165 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5166 }
5167 ComPtr<IProgress> pProgress2;
5168 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5169 if (FAILED(rc)) throw rc;
5170 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5171 if (FAILED(rc)) throw rc;
5172 /* Check the result of the asynchrony process. */
5173 LONG iRc;
5174 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5175 if (FAILED(rc)) throw rc;
5176 /* If the thread of the progress object has an error, then
5177 * retrieve the error info from there, or it'll be lost. */
5178 if (FAILED(iRc))
5179 throw setError(ProgressErrorInfo(pProgress2));
5180 }
5181 setMachineState(oldState);
5182 alock.acquire();
5183
5184 // delete the files pushed on the task list by Machine::Delete()
5185 // (this includes saved states of the machine and snapshots and
5186 // medium storage files from the IMedium list passed in, and the
5187 // machine XML file)
5188 StringsList::const_iterator it = task.llFilesToDelete.begin();
5189 while (it != task.llFilesToDelete.end())
5190 {
5191 const Utf8Str &strFile = *it;
5192 LogFunc(("Deleting file %s\n", strFile.c_str()));
5193 int vrc = RTFileDelete(strFile.c_str());
5194 if (RT_FAILURE(vrc))
5195 throw setError(VBOX_E_IPRT_ERROR,
5196 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5197
5198 ++it;
5199 if (it == task.llFilesToDelete.end())
5200 {
5201 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5202 if (FAILED(rc)) throw rc;
5203 break;
5204 }
5205
5206 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5207 if (FAILED(rc)) throw rc;
5208 }
5209
5210 /* delete the settings only when the file actually exists */
5211 if (mData->pMachineConfigFile->fileExists())
5212 {
5213 /* Delete any backup or uncommitted XML files. Ignore failures.
5214 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5215 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5216 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5217 RTFileDelete(otherXml.c_str());
5218 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5219 RTFileDelete(otherXml.c_str());
5220
5221 /* delete the Logs folder, nothing important should be left
5222 * there (we don't check for errors because the user might have
5223 * some private files there that we don't want to delete) */
5224 Utf8Str logFolder;
5225 getLogFolder(logFolder);
5226 Assert(logFolder.length());
5227 if (RTDirExists(logFolder.c_str()))
5228 {
5229 /* Delete all VBox.log[.N] files from the Logs folder
5230 * (this must be in sync with the rotation logic in
5231 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5232 * files that may have been created by the GUI. */
5233 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5234 logFolder.c_str(), RTPATH_DELIMITER);
5235 RTFileDelete(log.c_str());
5236 log = Utf8StrFmt("%s%cVBox.png",
5237 logFolder.c_str(), RTPATH_DELIMITER);
5238 RTFileDelete(log.c_str());
5239 for (int i = uLogHistoryCount; i > 0; i--)
5240 {
5241 log = Utf8StrFmt("%s%cVBox.log.%d",
5242 logFolder.c_str(), RTPATH_DELIMITER, i);
5243 RTFileDelete(log.c_str());
5244 log = Utf8StrFmt("%s%cVBox.png.%d",
5245 logFolder.c_str(), RTPATH_DELIMITER, i);
5246 RTFileDelete(log.c_str());
5247 }
5248
5249 RTDirRemove(logFolder.c_str());
5250 }
5251
5252 /* delete the Snapshots folder, nothing important should be left
5253 * there (we don't check for errors because the user might have
5254 * some private files there that we don't want to delete) */
5255 Utf8Str strFullSnapshotFolder;
5256 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5257 Assert(!strFullSnapshotFolder.isEmpty());
5258 if (RTDirExists(strFullSnapshotFolder.c_str()))
5259 RTDirRemove(strFullSnapshotFolder.c_str());
5260
5261 // delete the directory that contains the settings file, but only
5262 // if it matches the VM name
5263 Utf8Str settingsDir;
5264 if (isInOwnDir(&settingsDir))
5265 RTDirRemove(settingsDir.c_str());
5266 }
5267
5268 alock.release();
5269
5270 mParent->saveModifiedRegistries();
5271 }
5272 catch (HRESULT aRC) { rc = aRC; }
5273
5274 return rc;
5275}
5276
5277STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5278{
5279 CheckComArgOutPointerValid(aSnapshot);
5280
5281 AutoCaller autoCaller(this);
5282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5283
5284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5285
5286 ComObjPtr<Snapshot> pSnapshot;
5287 HRESULT rc;
5288
5289 if (!aNameOrId || !*aNameOrId)
5290 // null case (caller wants root snapshot): findSnapshotById() handles this
5291 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5292 else
5293 {
5294 Guid uuid(aNameOrId);
5295 if (!uuid.isEmpty())
5296 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5297 else
5298 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5299 }
5300 pSnapshot.queryInterfaceTo(aSnapshot);
5301
5302 return rc;
5303}
5304
5305STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5306{
5307 CheckComArgStrNotEmptyOrNull(aName);
5308 CheckComArgStrNotEmptyOrNull(aHostPath);
5309
5310 AutoCaller autoCaller(this);
5311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5312
5313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5314
5315 HRESULT rc = checkStateDependency(MutableStateDep);
5316 if (FAILED(rc)) return rc;
5317
5318 Utf8Str strName(aName);
5319
5320 ComObjPtr<SharedFolder> sharedFolder;
5321 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5322 if (SUCCEEDED(rc))
5323 return setError(VBOX_E_OBJECT_IN_USE,
5324 tr("Shared folder named '%s' already exists"),
5325 strName.c_str());
5326
5327 sharedFolder.createObject();
5328 rc = sharedFolder->init(getMachine(),
5329 strName,
5330 aHostPath,
5331 !!aWritable,
5332 !!aAutoMount,
5333 true /* fFailOnError */);
5334 if (FAILED(rc)) return rc;
5335
5336 setModified(IsModified_SharedFolders);
5337 mHWData.backup();
5338 mHWData->mSharedFolders.push_back(sharedFolder);
5339
5340 /* inform the direct session if any */
5341 alock.release();
5342 onSharedFolderChange();
5343
5344 return S_OK;
5345}
5346
5347STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5348{
5349 CheckComArgStrNotEmptyOrNull(aName);
5350
5351 AutoCaller autoCaller(this);
5352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5353
5354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5355
5356 HRESULT rc = checkStateDependency(MutableStateDep);
5357 if (FAILED(rc)) return rc;
5358
5359 ComObjPtr<SharedFolder> sharedFolder;
5360 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5361 if (FAILED(rc)) return rc;
5362
5363 setModified(IsModified_SharedFolders);
5364 mHWData.backup();
5365 mHWData->mSharedFolders.remove(sharedFolder);
5366
5367 /* inform the direct session if any */
5368 alock.release();
5369 onSharedFolderChange();
5370
5371 return S_OK;
5372}
5373
5374STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5375{
5376 CheckComArgOutPointerValid(aCanShow);
5377
5378 /* start with No */
5379 *aCanShow = FALSE;
5380
5381 AutoCaller autoCaller(this);
5382 AssertComRCReturnRC(autoCaller.rc());
5383
5384 ComPtr<IInternalSessionControl> directControl;
5385 {
5386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 if (mData->mSession.mState != SessionState_Locked)
5389 return setError(VBOX_E_INVALID_VM_STATE,
5390 tr("Machine is not locked for session (session state: %s)"),
5391 Global::stringifySessionState(mData->mSession.mState));
5392
5393 directControl = mData->mSession.mDirectControl;
5394 }
5395
5396 /* ignore calls made after #OnSessionEnd() is called */
5397 if (!directControl)
5398 return S_OK;
5399
5400 LONG64 dummy;
5401 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5402}
5403
5404STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5405{
5406 CheckComArgOutPointerValid(aWinId);
5407
5408 AutoCaller autoCaller(this);
5409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5410
5411 ComPtr<IInternalSessionControl> directControl;
5412 {
5413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5414
5415 if (mData->mSession.mState != SessionState_Locked)
5416 return setError(E_FAIL,
5417 tr("Machine is not locked for session (session state: %s)"),
5418 Global::stringifySessionState(mData->mSession.mState));
5419
5420 directControl = mData->mSession.mDirectControl;
5421 }
5422
5423 /* ignore calls made after #OnSessionEnd() is called */
5424 if (!directControl)
5425 return S_OK;
5426
5427 BOOL dummy;
5428 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5429}
5430
5431#ifdef VBOX_WITH_GUEST_PROPS
5432/**
5433 * Look up a guest property in VBoxSVC's internal structures.
5434 */
5435HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5436 BSTR *aValue,
5437 LONG64 *aTimestamp,
5438 BSTR *aFlags) const
5439{
5440 using namespace guestProp;
5441
5442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5443 Utf8Str strName(aName);
5444 HWData::GuestPropertyList::const_iterator it;
5445
5446 for (it = mHWData->mGuestProperties.begin();
5447 it != mHWData->mGuestProperties.end(); ++it)
5448 {
5449 if (it->strName == strName)
5450 {
5451 char szFlags[MAX_FLAGS_LEN + 1];
5452 it->strValue.cloneTo(aValue);
5453 *aTimestamp = it->mTimestamp;
5454 writeFlags(it->mFlags, szFlags);
5455 Bstr(szFlags).cloneTo(aFlags);
5456 break;
5457 }
5458 }
5459 return S_OK;
5460}
5461
5462/**
5463 * Query the VM that a guest property belongs to for the property.
5464 * @returns E_ACCESSDENIED if the VM process is not available or not
5465 * currently handling queries and the lookup should then be done in
5466 * VBoxSVC.
5467 */
5468HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5469 BSTR *aValue,
5470 LONG64 *aTimestamp,
5471 BSTR *aFlags) const
5472{
5473 HRESULT rc;
5474 ComPtr<IInternalSessionControl> directControl;
5475 directControl = mData->mSession.mDirectControl;
5476
5477 /* fail if we were called after #OnSessionEnd() is called. This is a
5478 * silly race condition. */
5479
5480 if (!directControl)
5481 rc = E_ACCESSDENIED;
5482 else
5483 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5484 false /* isSetter */,
5485 aValue, aTimestamp, aFlags);
5486 return rc;
5487}
5488#endif // VBOX_WITH_GUEST_PROPS
5489
5490STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5491 BSTR *aValue,
5492 LONG64 *aTimestamp,
5493 BSTR *aFlags)
5494{
5495#ifndef VBOX_WITH_GUEST_PROPS
5496 ReturnComNotImplemented();
5497#else // VBOX_WITH_GUEST_PROPS
5498 CheckComArgStrNotEmptyOrNull(aName);
5499 CheckComArgOutPointerValid(aValue);
5500 CheckComArgOutPointerValid(aTimestamp);
5501 CheckComArgOutPointerValid(aFlags);
5502
5503 AutoCaller autoCaller(this);
5504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5505
5506 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5507 if (rc == E_ACCESSDENIED)
5508 /* The VM is not running or the service is not (yet) accessible */
5509 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5510 return rc;
5511#endif // VBOX_WITH_GUEST_PROPS
5512}
5513
5514STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5515{
5516 LONG64 dummyTimestamp;
5517 Bstr dummyFlags;
5518 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5519}
5520
5521STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5522{
5523 Bstr dummyValue;
5524 Bstr dummyFlags;
5525 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5526}
5527
5528#ifdef VBOX_WITH_GUEST_PROPS
5529/**
5530 * Set a guest property in VBoxSVC's internal structures.
5531 */
5532HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5533 IN_BSTR aFlags)
5534{
5535 using namespace guestProp;
5536
5537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5538 HRESULT rc = S_OK;
5539 HWData::GuestProperty property;
5540 property.mFlags = NILFLAG;
5541 bool found = false;
5542
5543 rc = checkStateDependency(MutableStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 try
5547 {
5548 Utf8Str utf8Name(aName);
5549 Utf8Str utf8Flags(aFlags);
5550 uint32_t fFlags = NILFLAG;
5551 if ( (aFlags != NULL)
5552 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5553 )
5554 return setError(E_INVALIDARG,
5555 tr("Invalid flag values: '%ls'"),
5556 aFlags);
5557
5558 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5559 * know, this is simple and do an OK job atm.) */
5560 HWData::GuestPropertyList::iterator it;
5561 for (it = mHWData->mGuestProperties.begin();
5562 it != mHWData->mGuestProperties.end(); ++it)
5563 if (it->strName == utf8Name)
5564 {
5565 property = *it;
5566 if (it->mFlags & (RDONLYHOST))
5567 rc = setError(E_ACCESSDENIED,
5568 tr("The property '%ls' cannot be changed by the host"),
5569 aName);
5570 else
5571 {
5572 setModified(IsModified_MachineData);
5573 mHWData.backup(); // @todo r=dj backup in a loop?!?
5574
5575 /* The backup() operation invalidates our iterator, so
5576 * get a new one. */
5577 for (it = mHWData->mGuestProperties.begin();
5578 it->strName != utf8Name;
5579 ++it)
5580 ;
5581 mHWData->mGuestProperties.erase(it);
5582 }
5583 found = true;
5584 break;
5585 }
5586 if (found && SUCCEEDED(rc))
5587 {
5588 if (aValue)
5589 {
5590 RTTIMESPEC time;
5591 property.strValue = aValue;
5592 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5593 if (aFlags != NULL)
5594 property.mFlags = fFlags;
5595 mHWData->mGuestProperties.push_back(property);
5596 }
5597 }
5598 else if (SUCCEEDED(rc) && aValue)
5599 {
5600 RTTIMESPEC time;
5601 setModified(IsModified_MachineData);
5602 mHWData.backup();
5603 property.strName = aName;
5604 property.strValue = aValue;
5605 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5606 property.mFlags = fFlags;
5607 mHWData->mGuestProperties.push_back(property);
5608 }
5609 if ( SUCCEEDED(rc)
5610 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5611 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5612 RTSTR_MAX,
5613 utf8Name.c_str(),
5614 RTSTR_MAX,
5615 NULL)
5616 )
5617 )
5618 {
5619 /** @todo r=bird: Why aren't we leaving the lock here? The
5620 * same code in PushGuestProperty does... */
5621 mParent->onGuestPropertyChange(mData->mUuid, aName,
5622 aValue ? aValue : Bstr("").raw(),
5623 aFlags ? aFlags : Bstr("").raw());
5624 }
5625 }
5626 catch (std::bad_alloc &)
5627 {
5628 rc = E_OUTOFMEMORY;
5629 }
5630
5631 return rc;
5632}
5633
5634/**
5635 * Set a property on the VM that that property belongs to.
5636 * @returns E_ACCESSDENIED if the VM process is not available or not
5637 * currently handling queries and the setting should then be done in
5638 * VBoxSVC.
5639 */
5640HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5641 IN_BSTR aFlags)
5642{
5643 HRESULT rc;
5644
5645 try
5646 {
5647 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5648
5649 BSTR dummy = NULL; /* will not be changed (setter) */
5650 LONG64 dummy64;
5651 if (!directControl)
5652 rc = E_ACCESSDENIED;
5653 else
5654 /** @todo Fix when adding DeleteGuestProperty(),
5655 see defect. */
5656 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5657 true /* isSetter */,
5658 &dummy, &dummy64, &dummy);
5659 }
5660 catch (std::bad_alloc &)
5661 {
5662 rc = E_OUTOFMEMORY;
5663 }
5664
5665 return rc;
5666}
5667#endif // VBOX_WITH_GUEST_PROPS
5668
5669STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5670 IN_BSTR aFlags)
5671{
5672#ifndef VBOX_WITH_GUEST_PROPS
5673 ReturnComNotImplemented();
5674#else // VBOX_WITH_GUEST_PROPS
5675 CheckComArgStrNotEmptyOrNull(aName);
5676 CheckComArgMaybeNull(aFlags);
5677 CheckComArgMaybeNull(aValue);
5678
5679 AutoCaller autoCaller(this);
5680 if (FAILED(autoCaller.rc()))
5681 return autoCaller.rc();
5682
5683 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5684 if (rc == E_ACCESSDENIED)
5685 /* The VM is not running or the service is not (yet) accessible */
5686 rc = setGuestPropertyToService(aName, aValue, aFlags);
5687 return rc;
5688#endif // VBOX_WITH_GUEST_PROPS
5689}
5690
5691STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5692{
5693 return SetGuestProperty(aName, aValue, NULL);
5694}
5695
5696STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5697{
5698 return SetGuestProperty(aName, NULL, NULL);
5699}
5700
5701#ifdef VBOX_WITH_GUEST_PROPS
5702/**
5703 * Enumerate the guest properties in VBoxSVC's internal structures.
5704 */
5705HRESULT Machine::enumerateGuestPropertiesInService
5706 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5707 ComSafeArrayOut(BSTR, aValues),
5708 ComSafeArrayOut(LONG64, aTimestamps),
5709 ComSafeArrayOut(BSTR, aFlags))
5710{
5711 using namespace guestProp;
5712
5713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5714 Utf8Str strPatterns(aPatterns);
5715
5716 /*
5717 * Look for matching patterns and build up a list.
5718 */
5719 HWData::GuestPropertyList propList;
5720 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5721 it != mHWData->mGuestProperties.end();
5722 ++it)
5723 if ( strPatterns.isEmpty()
5724 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5725 RTSTR_MAX,
5726 it->strName.c_str(),
5727 RTSTR_MAX,
5728 NULL)
5729 )
5730 propList.push_back(*it);
5731
5732 /*
5733 * And build up the arrays for returning the property information.
5734 */
5735 size_t cEntries = propList.size();
5736 SafeArray<BSTR> names(cEntries);
5737 SafeArray<BSTR> values(cEntries);
5738 SafeArray<LONG64> timestamps(cEntries);
5739 SafeArray<BSTR> flags(cEntries);
5740 size_t iProp = 0;
5741 for (HWData::GuestPropertyList::iterator it = propList.begin();
5742 it != propList.end();
5743 ++it)
5744 {
5745 char szFlags[MAX_FLAGS_LEN + 1];
5746 it->strName.cloneTo(&names[iProp]);
5747 it->strValue.cloneTo(&values[iProp]);
5748 timestamps[iProp] = it->mTimestamp;
5749 writeFlags(it->mFlags, szFlags);
5750 Bstr(szFlags).cloneTo(&flags[iProp]);
5751 ++iProp;
5752 }
5753 names.detachTo(ComSafeArrayOutArg(aNames));
5754 values.detachTo(ComSafeArrayOutArg(aValues));
5755 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5756 flags.detachTo(ComSafeArrayOutArg(aFlags));
5757 return S_OK;
5758}
5759
5760/**
5761 * Enumerate the properties managed by a VM.
5762 * @returns E_ACCESSDENIED if the VM process is not available or not
5763 * currently handling queries and the setting should then be done in
5764 * VBoxSVC.
5765 */
5766HRESULT Machine::enumerateGuestPropertiesOnVM
5767 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5768 ComSafeArrayOut(BSTR, aValues),
5769 ComSafeArrayOut(LONG64, aTimestamps),
5770 ComSafeArrayOut(BSTR, aFlags))
5771{
5772 HRESULT rc;
5773 ComPtr<IInternalSessionControl> directControl;
5774 directControl = mData->mSession.mDirectControl;
5775
5776 if (!directControl)
5777 rc = E_ACCESSDENIED;
5778 else
5779 rc = directControl->EnumerateGuestProperties
5780 (aPatterns, ComSafeArrayOutArg(aNames),
5781 ComSafeArrayOutArg(aValues),
5782 ComSafeArrayOutArg(aTimestamps),
5783 ComSafeArrayOutArg(aFlags));
5784 return rc;
5785}
5786#endif // VBOX_WITH_GUEST_PROPS
5787
5788STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5789 ComSafeArrayOut(BSTR, aNames),
5790 ComSafeArrayOut(BSTR, aValues),
5791 ComSafeArrayOut(LONG64, aTimestamps),
5792 ComSafeArrayOut(BSTR, aFlags))
5793{
5794#ifndef VBOX_WITH_GUEST_PROPS
5795 ReturnComNotImplemented();
5796#else // VBOX_WITH_GUEST_PROPS
5797 CheckComArgMaybeNull(aPatterns);
5798 CheckComArgOutSafeArrayPointerValid(aNames);
5799 CheckComArgOutSafeArrayPointerValid(aValues);
5800 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5801 CheckComArgOutSafeArrayPointerValid(aFlags);
5802
5803 AutoCaller autoCaller(this);
5804 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5805
5806 HRESULT rc = enumerateGuestPropertiesOnVM
5807 (aPatterns, ComSafeArrayOutArg(aNames),
5808 ComSafeArrayOutArg(aValues),
5809 ComSafeArrayOutArg(aTimestamps),
5810 ComSafeArrayOutArg(aFlags));
5811 if (rc == E_ACCESSDENIED)
5812 /* The VM is not running or the service is not (yet) accessible */
5813 rc = enumerateGuestPropertiesInService
5814 (aPatterns, ComSafeArrayOutArg(aNames),
5815 ComSafeArrayOutArg(aValues),
5816 ComSafeArrayOutArg(aTimestamps),
5817 ComSafeArrayOutArg(aFlags));
5818 return rc;
5819#endif // VBOX_WITH_GUEST_PROPS
5820}
5821
5822STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5823 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5824{
5825 MediaData::AttachmentList atts;
5826
5827 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5828 if (FAILED(rc)) return rc;
5829
5830 SafeIfaceArray<IMediumAttachment> attachments(atts);
5831 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5832
5833 return S_OK;
5834}
5835
5836STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5837 LONG aControllerPort,
5838 LONG aDevice,
5839 IMediumAttachment **aAttachment)
5840{
5841 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5842 aControllerName, aControllerPort, aDevice));
5843
5844 CheckComArgStrNotEmptyOrNull(aControllerName);
5845 CheckComArgOutPointerValid(aAttachment);
5846
5847 AutoCaller autoCaller(this);
5848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5849
5850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5851
5852 *aAttachment = NULL;
5853
5854 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5855 aControllerName,
5856 aControllerPort,
5857 aDevice);
5858 if (pAttach.isNull())
5859 return setError(VBOX_E_OBJECT_NOT_FOUND,
5860 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5861 aDevice, aControllerPort, aControllerName);
5862
5863 pAttach.queryInterfaceTo(aAttachment);
5864
5865 return S_OK;
5866}
5867
5868STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5869 StorageBus_T aConnectionType,
5870 IStorageController **controller)
5871{
5872 CheckComArgStrNotEmptyOrNull(aName);
5873
5874 if ( (aConnectionType <= StorageBus_Null)
5875 || (aConnectionType > StorageBus_SAS))
5876 return setError(E_INVALIDARG,
5877 tr("Invalid connection type: %d"),
5878 aConnectionType);
5879
5880 AutoCaller autoCaller(this);
5881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5882
5883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5884
5885 HRESULT rc = checkStateDependency(MutableStateDep);
5886 if (FAILED(rc)) return rc;
5887
5888 /* try to find one with the name first. */
5889 ComObjPtr<StorageController> ctrl;
5890
5891 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5892 if (SUCCEEDED(rc))
5893 return setError(VBOX_E_OBJECT_IN_USE,
5894 tr("Storage controller named '%ls' already exists"),
5895 aName);
5896
5897 ctrl.createObject();
5898
5899 /* get a new instance number for the storage controller */
5900 ULONG ulInstance = 0;
5901 bool fBootable = true;
5902 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5903 it != mStorageControllers->end();
5904 ++it)
5905 {
5906 if ((*it)->getStorageBus() == aConnectionType)
5907 {
5908 ULONG ulCurInst = (*it)->getInstance();
5909
5910 if (ulCurInst >= ulInstance)
5911 ulInstance = ulCurInst + 1;
5912
5913 /* Only one controller of each type can be marked as bootable. */
5914 if ((*it)->getBootable())
5915 fBootable = false;
5916 }
5917 }
5918
5919 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5920 if (FAILED(rc)) return rc;
5921
5922 setModified(IsModified_Storage);
5923 mStorageControllers.backup();
5924 mStorageControllers->push_back(ctrl);
5925
5926 ctrl.queryInterfaceTo(controller);
5927
5928 /* inform the direct session if any */
5929 alock.release();
5930 onStorageControllerChange();
5931
5932 return S_OK;
5933}
5934
5935STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5936 IStorageController **aStorageController)
5937{
5938 CheckComArgStrNotEmptyOrNull(aName);
5939
5940 AutoCaller autoCaller(this);
5941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5942
5943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5944
5945 ComObjPtr<StorageController> ctrl;
5946
5947 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5948 if (SUCCEEDED(rc))
5949 ctrl.queryInterfaceTo(aStorageController);
5950
5951 return rc;
5952}
5953
5954STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5955 IStorageController **aStorageController)
5956{
5957 AutoCaller autoCaller(this);
5958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5959
5960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5961
5962 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5963 it != mStorageControllers->end();
5964 ++it)
5965 {
5966 if ((*it)->getInstance() == aInstance)
5967 {
5968 (*it).queryInterfaceTo(aStorageController);
5969 return S_OK;
5970 }
5971 }
5972
5973 return setError(VBOX_E_OBJECT_NOT_FOUND,
5974 tr("Could not find a storage controller with instance number '%lu'"),
5975 aInstance);
5976}
5977
5978STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5979{
5980 AutoCaller autoCaller(this);
5981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5982
5983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5984
5985 HRESULT rc = checkStateDependency(MutableStateDep);
5986 if (FAILED(rc)) return rc;
5987
5988 ComObjPtr<StorageController> ctrl;
5989
5990 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5991 if (SUCCEEDED(rc))
5992 {
5993 /* Ensure that only one controller of each type is marked as bootable. */
5994 if (fBootable == TRUE)
5995 {
5996 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5997 it != mStorageControllers->end();
5998 ++it)
5999 {
6000 ComObjPtr<StorageController> aCtrl = (*it);
6001
6002 if ( (aCtrl->getName() != Utf8Str(aName))
6003 && aCtrl->getBootable() == TRUE
6004 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6005 && aCtrl->getControllerType() == ctrl->getControllerType())
6006 {
6007 aCtrl->setBootable(FALSE);
6008 break;
6009 }
6010 }
6011 }
6012
6013 if (SUCCEEDED(rc))
6014 {
6015 ctrl->setBootable(fBootable);
6016 setModified(IsModified_Storage);
6017 }
6018 }
6019
6020 if (SUCCEEDED(rc))
6021 {
6022 /* inform the direct session if any */
6023 alock.release();
6024 onStorageControllerChange();
6025 }
6026
6027 return rc;
6028}
6029
6030STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6031{
6032 CheckComArgStrNotEmptyOrNull(aName);
6033
6034 AutoCaller autoCaller(this);
6035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6036
6037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 HRESULT rc = checkStateDependency(MutableStateDep);
6040 if (FAILED(rc)) return rc;
6041
6042 ComObjPtr<StorageController> ctrl;
6043 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6044 if (FAILED(rc)) return rc;
6045
6046 {
6047 /* find all attached devices to the appropriate storage controller and detach them all */
6048 // make a temporary list because detachDevice invalidates iterators into
6049 // mMediaData->mAttachments
6050 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6051
6052 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6053 it != llAttachments2.end();
6054 ++it)
6055 {
6056 MediumAttachment *pAttachTemp = *it;
6057
6058 AutoCaller localAutoCaller(pAttachTemp);
6059 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6060
6061 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6062
6063 if (pAttachTemp->getControllerName() == aName)
6064 {
6065 rc = detachDevice(pAttachTemp, alock, NULL);
6066 if (FAILED(rc)) return rc;
6067 }
6068 }
6069 }
6070
6071 /* We can remove it now. */
6072 setModified(IsModified_Storage);
6073 mStorageControllers.backup();
6074
6075 ctrl->unshare();
6076
6077 mStorageControllers->remove(ctrl);
6078
6079 /* inform the direct session if any */
6080 alock.release();
6081 onStorageControllerChange();
6082
6083 return S_OK;
6084}
6085
6086STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6087 ULONG *puOriginX,
6088 ULONG *puOriginY,
6089 ULONG *puWidth,
6090 ULONG *puHeight,
6091 BOOL *pfEnabled)
6092{
6093 LogFlowThisFunc(("\n"));
6094
6095 CheckComArgNotNull(puOriginX);
6096 CheckComArgNotNull(puOriginY);
6097 CheckComArgNotNull(puWidth);
6098 CheckComArgNotNull(puHeight);
6099 CheckComArgNotNull(pfEnabled);
6100
6101 uint32_t u32OriginX= 0;
6102 uint32_t u32OriginY= 0;
6103 uint32_t u32Width = 0;
6104 uint32_t u32Height = 0;
6105 uint16_t u16Flags = 0;
6106
6107 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6108 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6109 if (RT_FAILURE(vrc))
6110 {
6111#ifdef RT_OS_WINDOWS
6112 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6113 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6114 * So just assign fEnable to TRUE again.
6115 * The right fix would be to change GUI API wrappers to make sure that parameters
6116 * are changed only if API succeeds.
6117 */
6118 *pfEnabled = TRUE;
6119#endif
6120 return setError(VBOX_E_IPRT_ERROR,
6121 tr("Saved guest size is not available (%Rrc)"),
6122 vrc);
6123 }
6124
6125 *puOriginX = u32OriginX;
6126 *puOriginY = u32OriginY;
6127 *puWidth = u32Width;
6128 *puHeight = u32Height;
6129 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6130
6131 return S_OK;
6132}
6133
6134STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6135{
6136 LogFlowThisFunc(("\n"));
6137
6138 CheckComArgNotNull(aSize);
6139 CheckComArgNotNull(aWidth);
6140 CheckComArgNotNull(aHeight);
6141
6142 if (aScreenId != 0)
6143 return E_NOTIMPL;
6144
6145 AutoCaller autoCaller(this);
6146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6147
6148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 uint8_t *pu8Data = NULL;
6151 uint32_t cbData = 0;
6152 uint32_t u32Width = 0;
6153 uint32_t u32Height = 0;
6154
6155 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6156
6157 if (RT_FAILURE(vrc))
6158 return setError(VBOX_E_IPRT_ERROR,
6159 tr("Saved screenshot data is not available (%Rrc)"),
6160 vrc);
6161
6162 *aSize = cbData;
6163 *aWidth = u32Width;
6164 *aHeight = u32Height;
6165
6166 freeSavedDisplayScreenshot(pu8Data);
6167
6168 return S_OK;
6169}
6170
6171STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6172{
6173 LogFlowThisFunc(("\n"));
6174
6175 CheckComArgNotNull(aWidth);
6176 CheckComArgNotNull(aHeight);
6177 CheckComArgOutSafeArrayPointerValid(aData);
6178
6179 if (aScreenId != 0)
6180 return E_NOTIMPL;
6181
6182 AutoCaller autoCaller(this);
6183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6184
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 uint8_t *pu8Data = NULL;
6188 uint32_t cbData = 0;
6189 uint32_t u32Width = 0;
6190 uint32_t u32Height = 0;
6191
6192 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6193
6194 if (RT_FAILURE(vrc))
6195 return setError(VBOX_E_IPRT_ERROR,
6196 tr("Saved screenshot data is not available (%Rrc)"),
6197 vrc);
6198
6199 *aWidth = u32Width;
6200 *aHeight = u32Height;
6201
6202 com::SafeArray<BYTE> bitmap(cbData);
6203 /* Convert pixels to format expected by the API caller. */
6204 if (aBGR)
6205 {
6206 /* [0] B, [1] G, [2] R, [3] A. */
6207 for (unsigned i = 0; i < cbData; i += 4)
6208 {
6209 bitmap[i] = pu8Data[i];
6210 bitmap[i + 1] = pu8Data[i + 1];
6211 bitmap[i + 2] = pu8Data[i + 2];
6212 bitmap[i + 3] = 0xff;
6213 }
6214 }
6215 else
6216 {
6217 /* [0] R, [1] G, [2] B, [3] A. */
6218 for (unsigned i = 0; i < cbData; i += 4)
6219 {
6220 bitmap[i] = pu8Data[i + 2];
6221 bitmap[i + 1] = pu8Data[i + 1];
6222 bitmap[i + 2] = pu8Data[i];
6223 bitmap[i + 3] = 0xff;
6224 }
6225 }
6226 bitmap.detachTo(ComSafeArrayOutArg(aData));
6227
6228 freeSavedDisplayScreenshot(pu8Data);
6229
6230 return S_OK;
6231}
6232
6233
6234STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6235{
6236 LogFlowThisFunc(("\n"));
6237
6238 CheckComArgNotNull(aWidth);
6239 CheckComArgNotNull(aHeight);
6240 CheckComArgOutSafeArrayPointerValid(aData);
6241
6242 if (aScreenId != 0)
6243 return E_NOTIMPL;
6244
6245 AutoCaller autoCaller(this);
6246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6247
6248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6249
6250 uint8_t *pu8Data = NULL;
6251 uint32_t cbData = 0;
6252 uint32_t u32Width = 0;
6253 uint32_t u32Height = 0;
6254
6255 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6256
6257 if (RT_FAILURE(vrc))
6258 return setError(VBOX_E_IPRT_ERROR,
6259 tr("Saved screenshot data is not available (%Rrc)"),
6260 vrc);
6261
6262 *aWidth = u32Width;
6263 *aHeight = u32Height;
6264
6265 HRESULT rc = S_OK;
6266 uint8_t *pu8PNG = NULL;
6267 uint32_t cbPNG = 0;
6268 uint32_t cxPNG = 0;
6269 uint32_t cyPNG = 0;
6270
6271 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6272
6273 if (RT_SUCCESS(vrc))
6274 {
6275 com::SafeArray<BYTE> screenData(cbPNG);
6276 screenData.initFrom(pu8PNG, cbPNG);
6277 if (pu8PNG)
6278 RTMemFree(pu8PNG);
6279 screenData.detachTo(ComSafeArrayOutArg(aData));
6280 }
6281 else
6282 {
6283 if (pu8PNG)
6284 RTMemFree(pu8PNG);
6285 return setError(VBOX_E_IPRT_ERROR,
6286 tr("Could not convert screenshot to PNG (%Rrc)"),
6287 vrc);
6288 }
6289
6290 freeSavedDisplayScreenshot(pu8Data);
6291
6292 return rc;
6293}
6294
6295STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6296{
6297 LogFlowThisFunc(("\n"));
6298
6299 CheckComArgNotNull(aSize);
6300 CheckComArgNotNull(aWidth);
6301 CheckComArgNotNull(aHeight);
6302
6303 if (aScreenId != 0)
6304 return E_NOTIMPL;
6305
6306 AutoCaller autoCaller(this);
6307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6308
6309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 uint8_t *pu8Data = NULL;
6312 uint32_t cbData = 0;
6313 uint32_t u32Width = 0;
6314 uint32_t u32Height = 0;
6315
6316 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6317
6318 if (RT_FAILURE(vrc))
6319 return setError(VBOX_E_IPRT_ERROR,
6320 tr("Saved screenshot data is not available (%Rrc)"),
6321 vrc);
6322
6323 *aSize = cbData;
6324 *aWidth = u32Width;
6325 *aHeight = u32Height;
6326
6327 freeSavedDisplayScreenshot(pu8Data);
6328
6329 return S_OK;
6330}
6331
6332STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6333{
6334 LogFlowThisFunc(("\n"));
6335
6336 CheckComArgNotNull(aWidth);
6337 CheckComArgNotNull(aHeight);
6338 CheckComArgOutSafeArrayPointerValid(aData);
6339
6340 if (aScreenId != 0)
6341 return E_NOTIMPL;
6342
6343 AutoCaller autoCaller(this);
6344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6345
6346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 uint8_t *pu8Data = NULL;
6349 uint32_t cbData = 0;
6350 uint32_t u32Width = 0;
6351 uint32_t u32Height = 0;
6352
6353 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6354
6355 if (RT_FAILURE(vrc))
6356 return setError(VBOX_E_IPRT_ERROR,
6357 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6358 vrc);
6359
6360 *aWidth = u32Width;
6361 *aHeight = u32Height;
6362
6363 com::SafeArray<BYTE> png(cbData);
6364 png.initFrom(pu8Data, cbData);
6365 png.detachTo(ComSafeArrayOutArg(aData));
6366
6367 freeSavedDisplayScreenshot(pu8Data);
6368
6369 return S_OK;
6370}
6371
6372STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6373{
6374 HRESULT rc = S_OK;
6375 LogFlowThisFunc(("\n"));
6376
6377 AutoCaller autoCaller(this);
6378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6379
6380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 if (!mHWData->mCPUHotPlugEnabled)
6383 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6384
6385 if (aCpu >= mHWData->mCPUCount)
6386 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6387
6388 if (mHWData->mCPUAttached[aCpu])
6389 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6390
6391 alock.release();
6392 rc = onCPUChange(aCpu, false);
6393 alock.acquire();
6394 if (FAILED(rc)) return rc;
6395
6396 setModified(IsModified_MachineData);
6397 mHWData.backup();
6398 mHWData->mCPUAttached[aCpu] = true;
6399
6400 /* Save settings if online */
6401 if (Global::IsOnline(mData->mMachineState))
6402 saveSettings(NULL);
6403
6404 return S_OK;
6405}
6406
6407STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6408{
6409 HRESULT rc = S_OK;
6410 LogFlowThisFunc(("\n"));
6411
6412 AutoCaller autoCaller(this);
6413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6414
6415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6416
6417 if (!mHWData->mCPUHotPlugEnabled)
6418 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6419
6420 if (aCpu >= SchemaDefs::MaxCPUCount)
6421 return setError(E_INVALIDARG,
6422 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6423 SchemaDefs::MaxCPUCount);
6424
6425 if (!mHWData->mCPUAttached[aCpu])
6426 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6427
6428 /* CPU 0 can't be detached */
6429 if (aCpu == 0)
6430 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6431
6432 alock.release();
6433 rc = onCPUChange(aCpu, true);
6434 alock.acquire();
6435 if (FAILED(rc)) return rc;
6436
6437 setModified(IsModified_MachineData);
6438 mHWData.backup();
6439 mHWData->mCPUAttached[aCpu] = false;
6440
6441 /* Save settings if online */
6442 if (Global::IsOnline(mData->mMachineState))
6443 saveSettings(NULL);
6444
6445 return S_OK;
6446}
6447
6448STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6449{
6450 LogFlowThisFunc(("\n"));
6451
6452 CheckComArgNotNull(aCpuAttached);
6453
6454 *aCpuAttached = false;
6455
6456 AutoCaller autoCaller(this);
6457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6458
6459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 /* If hotplug is enabled the CPU is always enabled. */
6462 if (!mHWData->mCPUHotPlugEnabled)
6463 {
6464 if (aCpu < mHWData->mCPUCount)
6465 *aCpuAttached = true;
6466 }
6467 else
6468 {
6469 if (aCpu < SchemaDefs::MaxCPUCount)
6470 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6471 }
6472
6473 return S_OK;
6474}
6475
6476STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6477{
6478 CheckComArgOutPointerValid(aName);
6479
6480 AutoCaller autoCaller(this);
6481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6482
6483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6484
6485 Utf8Str log = queryLogFilename(aIdx);
6486 if (!RTFileExists(log.c_str()))
6487 log.setNull();
6488 log.cloneTo(aName);
6489
6490 return S_OK;
6491}
6492
6493STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6494{
6495 LogFlowThisFunc(("\n"));
6496 CheckComArgOutSafeArrayPointerValid(aData);
6497 if (aSize < 0)
6498 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6499
6500 AutoCaller autoCaller(this);
6501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6502
6503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6504
6505 HRESULT rc = S_OK;
6506 Utf8Str log = queryLogFilename(aIdx);
6507
6508 /* do not unnecessarily hold the lock while doing something which does
6509 * not need the lock and potentially takes a long time. */
6510 alock.release();
6511
6512 /* Limit the chunk size to 32K for now, as that gives better performance
6513 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6514 * One byte expands to approx. 25 bytes of breathtaking XML. */
6515 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6516 com::SafeArray<BYTE> logData(cbData);
6517
6518 RTFILE LogFile;
6519 int vrc = RTFileOpen(&LogFile, log.c_str(),
6520 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6521 if (RT_SUCCESS(vrc))
6522 {
6523 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6524 if (RT_SUCCESS(vrc))
6525 logData.resize(cbData);
6526 else
6527 rc = setError(VBOX_E_IPRT_ERROR,
6528 tr("Could not read log file '%s' (%Rrc)"),
6529 log.c_str(), vrc);
6530 RTFileClose(LogFile);
6531 }
6532 else
6533 rc = setError(VBOX_E_IPRT_ERROR,
6534 tr("Could not open log file '%s' (%Rrc)"),
6535 log.c_str(), vrc);
6536
6537 if (FAILED(rc))
6538 logData.resize(0);
6539 logData.detachTo(ComSafeArrayOutArg(aData));
6540
6541 return rc;
6542}
6543
6544
6545/**
6546 * Currently this method doesn't attach device to the running VM,
6547 * just makes sure it's plugged on next VM start.
6548 */
6549STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6550{
6551 AutoCaller autoCaller(this);
6552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6553
6554 // lock scope
6555 {
6556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 HRESULT rc = checkStateDependency(MutableStateDep);
6559 if (FAILED(rc)) return rc;
6560
6561 ChipsetType_T aChipset = ChipsetType_PIIX3;
6562 COMGETTER(ChipsetType)(&aChipset);
6563
6564 if (aChipset != ChipsetType_ICH9)
6565 {
6566 return setError(E_INVALIDARG,
6567 tr("Host PCI attachment only supported with ICH9 chipset"));
6568 }
6569
6570 // check if device with this host PCI address already attached
6571 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6572 it != mHWData->mPCIDeviceAssignments.end();
6573 ++it)
6574 {
6575 LONG iHostAddress = -1;
6576 ComPtr<PCIDeviceAttachment> pAttach;
6577 pAttach = *it;
6578 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6579 if (iHostAddress == hostAddress)
6580 return setError(E_INVALIDARG,
6581 tr("Device with host PCI address already attached to this VM"));
6582 }
6583
6584 ComObjPtr<PCIDeviceAttachment> pda;
6585 char name[32];
6586
6587 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6588 Bstr bname(name);
6589 pda.createObject();
6590 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6591 setModified(IsModified_MachineData);
6592 mHWData.backup();
6593 mHWData->mPCIDeviceAssignments.push_back(pda);
6594 }
6595
6596 return S_OK;
6597}
6598
6599/**
6600 * Currently this method doesn't detach device from the running VM,
6601 * just makes sure it's not plugged on next VM start.
6602 */
6603STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6604{
6605 AutoCaller autoCaller(this);
6606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6607
6608 ComObjPtr<PCIDeviceAttachment> pAttach;
6609 bool fRemoved = false;
6610 HRESULT rc;
6611
6612 // lock scope
6613 {
6614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6615
6616 rc = checkStateDependency(MutableStateDep);
6617 if (FAILED(rc)) return rc;
6618
6619 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6620 it != mHWData->mPCIDeviceAssignments.end();
6621 ++it)
6622 {
6623 LONG iHostAddress = -1;
6624 pAttach = *it;
6625 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6626 if (iHostAddress != -1 && iHostAddress == hostAddress)
6627 {
6628 setModified(IsModified_MachineData);
6629 mHWData.backup();
6630 mHWData->mPCIDeviceAssignments.remove(pAttach);
6631 fRemoved = true;
6632 break;
6633 }
6634 }
6635 }
6636
6637
6638 /* Fire event outside of the lock */
6639 if (fRemoved)
6640 {
6641 Assert(!pAttach.isNull());
6642 ComPtr<IEventSource> es;
6643 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6644 Assert(SUCCEEDED(rc));
6645 Bstr mid;
6646 rc = this->COMGETTER(Id)(mid.asOutParam());
6647 Assert(SUCCEEDED(rc));
6648 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6649 }
6650
6651 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6652 tr("No host PCI device %08x attached"),
6653 hostAddress
6654 );
6655}
6656
6657STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6658{
6659 CheckComArgOutSafeArrayPointerValid(aAssignments);
6660
6661 AutoCaller autoCaller(this);
6662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6663
6664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6665
6666 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6667 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6668
6669 return S_OK;
6670}
6671
6672STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6673{
6674 CheckComArgOutPointerValid(aBandwidthControl);
6675
6676 AutoCaller autoCaller(this);
6677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6678
6679 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6680
6681 return S_OK;
6682}
6683
6684STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6685{
6686 CheckComArgOutPointerValid(pfEnabled);
6687 AutoCaller autoCaller(this);
6688 HRESULT hrc = autoCaller.rc();
6689 if (SUCCEEDED(hrc))
6690 {
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6693 }
6694 return hrc;
6695}
6696
6697STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6698{
6699 AutoCaller autoCaller(this);
6700 HRESULT hrc = autoCaller.rc();
6701 if (SUCCEEDED(hrc))
6702 {
6703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6704 hrc = checkStateDependency(MutableStateDep);
6705 if (SUCCEEDED(hrc))
6706 {
6707 hrc = mHWData.backupEx();
6708 if (SUCCEEDED(hrc))
6709 {
6710 setModified(IsModified_MachineData);
6711 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6712 }
6713 }
6714 }
6715 return hrc;
6716}
6717
6718STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6719{
6720 CheckComArgOutPointerValid(pbstrConfig);
6721 AutoCaller autoCaller(this);
6722 HRESULT hrc = autoCaller.rc();
6723 if (SUCCEEDED(hrc))
6724 {
6725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6726 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6727 }
6728 return hrc;
6729}
6730
6731STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6732{
6733 CheckComArgStr(bstrConfig);
6734 AutoCaller autoCaller(this);
6735 HRESULT hrc = autoCaller.rc();
6736 if (SUCCEEDED(hrc))
6737 {
6738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6739 hrc = checkStateDependency(MutableStateDep);
6740 if (SUCCEEDED(hrc))
6741 {
6742 hrc = mHWData.backupEx();
6743 if (SUCCEEDED(hrc))
6744 {
6745 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6746 if (SUCCEEDED(hrc))
6747 setModified(IsModified_MachineData);
6748 }
6749 }
6750 }
6751 return hrc;
6752
6753}
6754
6755STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6756{
6757 CheckComArgOutPointerValid(pfAllow);
6758 AutoCaller autoCaller(this);
6759 HRESULT hrc = autoCaller.rc();
6760 if (SUCCEEDED(hrc))
6761 {
6762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6763 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6764 }
6765 return hrc;
6766}
6767
6768STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6769{
6770 AutoCaller autoCaller(this);
6771 HRESULT hrc = autoCaller.rc();
6772 if (SUCCEEDED(hrc))
6773 {
6774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6775 hrc = checkStateDependency(MutableStateDep);
6776 if (SUCCEEDED(hrc))
6777 {
6778 hrc = mHWData.backupEx();
6779 if (SUCCEEDED(hrc))
6780 {
6781 setModified(IsModified_MachineData);
6782 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6783 }
6784 }
6785 }
6786 return hrc;
6787}
6788
6789STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6790{
6791 CheckComArgOutPointerValid(pfEnabled);
6792 AutoCaller autoCaller(this);
6793 HRESULT hrc = autoCaller.rc();
6794 if (SUCCEEDED(hrc))
6795 {
6796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6797 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6798 }
6799 return hrc;
6800}
6801
6802STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6803{
6804 AutoCaller autoCaller(this);
6805 HRESULT hrc = autoCaller.rc();
6806 if (SUCCEEDED(hrc))
6807 {
6808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6809 hrc = checkStateDependency(MutableStateDep);
6810 if ( SUCCEEDED(hrc)
6811 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6812 {
6813 AutostartDb *autostartDb = mParent->getAutostartDb();
6814 int vrc;
6815
6816 if (fEnabled)
6817 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6818 else
6819 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6820
6821 if (RT_SUCCESS(vrc))
6822 {
6823 hrc = mHWData.backupEx();
6824 if (SUCCEEDED(hrc))
6825 {
6826 setModified(IsModified_MachineData);
6827 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6828 }
6829 }
6830 else if (vrc == VERR_NOT_SUPPORTED)
6831 hrc = setError(VBOX_E_NOT_SUPPORTED,
6832 tr("The VM autostart feature is not supported on this platform"));
6833 else if (vrc == VERR_PATH_NOT_FOUND)
6834 hrc = setError(E_FAIL,
6835 tr("The path to the autostart database is not set"));
6836 else
6837 hrc = setError(E_UNEXPECTED,
6838 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6839 fEnabled ? "Adding" : "Removing",
6840 mUserData->s.strName.c_str(), vrc);
6841 }
6842 }
6843 return hrc;
6844}
6845
6846STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6847{
6848 CheckComArgOutPointerValid(puDelay);
6849 AutoCaller autoCaller(this);
6850 HRESULT hrc = autoCaller.rc();
6851 if (SUCCEEDED(hrc))
6852 {
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 *puDelay = mHWData->mAutostart.uAutostartDelay;
6855 }
6856 return hrc;
6857}
6858
6859STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6860{
6861 AutoCaller autoCaller(this);
6862 HRESULT hrc = autoCaller.rc();
6863 if (SUCCEEDED(hrc))
6864 {
6865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6866 hrc = checkStateDependency(MutableStateDep);
6867 if (SUCCEEDED(hrc))
6868 {
6869 hrc = mHWData.backupEx();
6870 if (SUCCEEDED(hrc))
6871 {
6872 setModified(IsModified_MachineData);
6873 mHWData->mAutostart.uAutostartDelay = uDelay;
6874 }
6875 }
6876 }
6877 return hrc;
6878}
6879
6880STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6881{
6882 CheckComArgOutPointerValid(penmAutostopType);
6883 AutoCaller autoCaller(this);
6884 HRESULT hrc = autoCaller.rc();
6885 if (SUCCEEDED(hrc))
6886 {
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6889 }
6890 return hrc;
6891}
6892
6893STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6894{
6895 AutoCaller autoCaller(this);
6896 HRESULT hrc = autoCaller.rc();
6897 if (SUCCEEDED(hrc))
6898 {
6899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6900 hrc = checkStateDependency(MutableStateDep);
6901 if ( SUCCEEDED(hrc)
6902 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6903 {
6904 AutostartDb *autostartDb = mParent->getAutostartDb();
6905 int vrc;
6906
6907 if (enmAutostopType != AutostopType_Disabled)
6908 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6909 else
6910 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6911
6912 if (RT_SUCCESS(vrc))
6913 {
6914 hrc = mHWData.backupEx();
6915 if (SUCCEEDED(hrc))
6916 {
6917 setModified(IsModified_MachineData);
6918 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6919 }
6920 }
6921 else if (vrc == VERR_NOT_SUPPORTED)
6922 hrc = setError(VBOX_E_NOT_SUPPORTED,
6923 tr("The VM autostop feature is not supported on this platform"));
6924 else if (vrc == VERR_PATH_NOT_FOUND)
6925 hrc = setError(E_FAIL,
6926 tr("The path to the autostart database is not set"));
6927 else
6928 hrc = setError(E_UNEXPECTED,
6929 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6930 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6931 mUserData->s.strName.c_str(), vrc);
6932 }
6933 }
6934 return hrc;
6935}
6936
6937
6938STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6939{
6940 LogFlowFuncEnter();
6941
6942 CheckComArgNotNull(pTarget);
6943 CheckComArgOutPointerValid(pProgress);
6944
6945 /* Convert the options. */
6946 RTCList<CloneOptions_T> optList;
6947 if (options != NULL)
6948 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6949
6950 if (optList.contains(CloneOptions_Link))
6951 {
6952 if (!isSnapshotMachine())
6953 return setError(E_INVALIDARG,
6954 tr("Linked clone can only be created from a snapshot"));
6955 if (mode != CloneMode_MachineState)
6956 return setError(E_INVALIDARG,
6957 tr("Linked clone can only be created for a single machine state"));
6958 }
6959 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6960
6961 AutoCaller autoCaller(this);
6962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6963
6964
6965 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6966
6967 HRESULT rc = pWorker->start(pProgress);
6968
6969 LogFlowFuncLeave();
6970
6971 return rc;
6972}
6973
6974// public methods for internal purposes
6975/////////////////////////////////////////////////////////////////////////////
6976
6977/**
6978 * Adds the given IsModified_* flag to the dirty flags of the machine.
6979 * This must be called either during loadSettings or under the machine write lock.
6980 * @param fl
6981 */
6982void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6983{
6984 mData->flModifications |= fl;
6985 if (fAllowStateModification && isStateModificationAllowed())
6986 mData->mCurrentStateModified = true;
6987}
6988
6989/**
6990 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6991 * care of the write locking.
6992 *
6993 * @param fModifications The flag to add.
6994 */
6995void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6996{
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998 setModified(fModification, fAllowStateModification);
6999}
7000
7001/**
7002 * Saves the registry entry of this machine to the given configuration node.
7003 *
7004 * @param aEntryNode Node to save the registry entry to.
7005 *
7006 * @note locks this object for reading.
7007 */
7008HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7009{
7010 AutoLimitedCaller autoCaller(this);
7011 AssertComRCReturnRC(autoCaller.rc());
7012
7013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7014
7015 data.uuid = mData->mUuid;
7016 data.strSettingsFile = mData->m_strConfigFile;
7017
7018 return S_OK;
7019}
7020
7021/**
7022 * Calculates the absolute path of the given path taking the directory of the
7023 * machine settings file as the current directory.
7024 *
7025 * @param aPath Path to calculate the absolute path for.
7026 * @param aResult Where to put the result (used only on success, can be the
7027 * same Utf8Str instance as passed in @a aPath).
7028 * @return IPRT result.
7029 *
7030 * @note Locks this object for reading.
7031 */
7032int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7033{
7034 AutoCaller autoCaller(this);
7035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7036
7037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7040
7041 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7042
7043 strSettingsDir.stripFilename();
7044 char folder[RTPATH_MAX];
7045 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7046 if (RT_SUCCESS(vrc))
7047 aResult = folder;
7048
7049 return vrc;
7050}
7051
7052/**
7053 * Copies strSource to strTarget, making it relative to the machine folder
7054 * if it is a subdirectory thereof, or simply copying it otherwise.
7055 *
7056 * @param strSource Path to evaluate and copy.
7057 * @param strTarget Buffer to receive target path.
7058 *
7059 * @note Locks this object for reading.
7060 */
7061void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7062 Utf8Str &strTarget)
7063{
7064 AutoCaller autoCaller(this);
7065 AssertComRCReturn(autoCaller.rc(), (void)0);
7066
7067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7068
7069 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7070 // use strTarget as a temporary buffer to hold the machine settings dir
7071 strTarget = mData->m_strConfigFileFull;
7072 strTarget.stripFilename();
7073 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7074 {
7075 // is relative: then append what's left
7076 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7077 // for empty paths (only possible for subdirs) use "." to avoid
7078 // triggering default settings for not present config attributes.
7079 if (strTarget.isEmpty())
7080 strTarget = ".";
7081 }
7082 else
7083 // is not relative: then overwrite
7084 strTarget = strSource;
7085}
7086
7087/**
7088 * Returns the full path to the machine's log folder in the
7089 * \a aLogFolder argument.
7090 */
7091void Machine::getLogFolder(Utf8Str &aLogFolder)
7092{
7093 AutoCaller autoCaller(this);
7094 AssertComRCReturnVoid(autoCaller.rc());
7095
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 char szTmp[RTPATH_MAX];
7099 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7100 if (RT_SUCCESS(vrc))
7101 {
7102 if (szTmp[0] && !mUserData.isNull())
7103 {
7104 char szTmp2[RTPATH_MAX];
7105 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7106 if (RT_SUCCESS(vrc))
7107 aLogFolder = BstrFmt("%s%c%s",
7108 szTmp2,
7109 RTPATH_DELIMITER,
7110 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7111 }
7112 else
7113 vrc = VERR_PATH_IS_RELATIVE;
7114 }
7115
7116 if (RT_FAILURE(vrc))
7117 {
7118 // fallback if VBOX_USER_LOGHOME is not set or invalid
7119 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7120 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7121 aLogFolder.append(RTPATH_DELIMITER);
7122 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7123 }
7124}
7125
7126/**
7127 * Returns the full path to the machine's log file for an given index.
7128 */
7129Utf8Str Machine::queryLogFilename(ULONG idx)
7130{
7131 Utf8Str logFolder;
7132 getLogFolder(logFolder);
7133 Assert(logFolder.length());
7134 Utf8Str log;
7135 if (idx == 0)
7136 log = Utf8StrFmt("%s%cVBox.log",
7137 logFolder.c_str(), RTPATH_DELIMITER);
7138 else
7139 log = Utf8StrFmt("%s%cVBox.log.%d",
7140 logFolder.c_str(), RTPATH_DELIMITER, idx);
7141 return log;
7142}
7143
7144/**
7145 * Composes a unique saved state filename based on the current system time. The filename is
7146 * granular to the second so this will work so long as no more than one snapshot is taken on
7147 * a machine per second.
7148 *
7149 * Before version 4.1, we used this formula for saved state files:
7150 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7151 * which no longer works because saved state files can now be shared between the saved state of the
7152 * "saved" machine and an online snapshot, and the following would cause problems:
7153 * 1) save machine
7154 * 2) create online snapshot from that machine state --> reusing saved state file
7155 * 3) save machine again --> filename would be reused, breaking the online snapshot
7156 *
7157 * So instead we now use a timestamp.
7158 *
7159 * @param str
7160 */
7161void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturnVoid(autoCaller.rc());
7165
7166 {
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7169 }
7170
7171 RTTIMESPEC ts;
7172 RTTimeNow(&ts);
7173 RTTIME time;
7174 RTTimeExplode(&time, &ts);
7175
7176 strStateFilePath += RTPATH_DELIMITER;
7177 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7178 time.i32Year, time.u8Month, time.u8MonthDay,
7179 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7180}
7181
7182/**
7183 * @note Locks this object for writing, calls the client process
7184 * (inside the lock).
7185 */
7186HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7187 const Utf8Str &strType,
7188 const Utf8Str &strEnvironment,
7189 ProgressProxy *aProgress)
7190{
7191 LogFlowThisFuncEnter();
7192
7193 AssertReturn(aControl, E_FAIL);
7194 AssertReturn(aProgress, E_FAIL);
7195
7196 AutoCaller autoCaller(this);
7197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7198
7199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 if (!mData->mRegistered)
7202 return setError(E_UNEXPECTED,
7203 tr("The machine '%s' is not registered"),
7204 mUserData->s.strName.c_str());
7205
7206 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7207
7208 if ( mData->mSession.mState == SessionState_Locked
7209 || mData->mSession.mState == SessionState_Spawning
7210 || mData->mSession.mState == SessionState_Unlocking)
7211 return setError(VBOX_E_INVALID_OBJECT_STATE,
7212 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7213 mUserData->s.strName.c_str());
7214
7215 /* may not be busy */
7216 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7217
7218 /* get the path to the executable */
7219 char szPath[RTPATH_MAX];
7220 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7221 size_t sz = strlen(szPath);
7222 szPath[sz++] = RTPATH_DELIMITER;
7223 szPath[sz] = 0;
7224 char *cmd = szPath + sz;
7225 sz = RTPATH_MAX - sz;
7226
7227 int vrc = VINF_SUCCESS;
7228 RTPROCESS pid = NIL_RTPROCESS;
7229
7230 RTENV env = RTENV_DEFAULT;
7231
7232 if (!strEnvironment.isEmpty())
7233 {
7234 char *newEnvStr = NULL;
7235
7236 do
7237 {
7238 /* clone the current environment */
7239 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7240 AssertRCBreakStmt(vrc2, vrc = vrc2);
7241
7242 newEnvStr = RTStrDup(strEnvironment.c_str());
7243 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7244
7245 /* put new variables to the environment
7246 * (ignore empty variable names here since RTEnv API
7247 * intentionally doesn't do that) */
7248 char *var = newEnvStr;
7249 for (char *p = newEnvStr; *p; ++p)
7250 {
7251 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7252 {
7253 *p = '\0';
7254 if (*var)
7255 {
7256 char *val = strchr(var, '=');
7257 if (val)
7258 {
7259 *val++ = '\0';
7260 vrc2 = RTEnvSetEx(env, var, val);
7261 }
7262 else
7263 vrc2 = RTEnvUnsetEx(env, var);
7264 if (RT_FAILURE(vrc2))
7265 break;
7266 }
7267 var = p + 1;
7268 }
7269 }
7270 if (RT_SUCCESS(vrc2) && *var)
7271 vrc2 = RTEnvPutEx(env, var);
7272
7273 AssertRCBreakStmt(vrc2, vrc = vrc2);
7274 }
7275 while (0);
7276
7277 if (newEnvStr != NULL)
7278 RTStrFree(newEnvStr);
7279 }
7280
7281 /* Qt is default */
7282#ifdef VBOX_WITH_QTGUI
7283 if (strType == "gui" || strType == "GUI/Qt")
7284 {
7285# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7286 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7287# else
7288 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7289# endif
7290 Assert(sz >= sizeof(VirtualBox_exe));
7291 strcpy(cmd, VirtualBox_exe);
7292
7293 Utf8Str idStr = mData->mUuid.toString();
7294 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7295 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7296 }
7297#else /* !VBOX_WITH_QTGUI */
7298 if (0)
7299 ;
7300#endif /* VBOX_WITH_QTGUI */
7301
7302 else
7303
7304#ifdef VBOX_WITH_VBOXSDL
7305 if (strType == "sdl" || strType == "GUI/SDL")
7306 {
7307 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7308 Assert(sz >= sizeof(VBoxSDL_exe));
7309 strcpy(cmd, VBoxSDL_exe);
7310
7311 Utf8Str idStr = mData->mUuid.toString();
7312 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7313 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7314 }
7315#else /* !VBOX_WITH_VBOXSDL */
7316 if (0)
7317 ;
7318#endif /* !VBOX_WITH_VBOXSDL */
7319
7320 else
7321
7322#ifdef VBOX_WITH_HEADLESS
7323 if ( strType == "headless"
7324 || strType == "capture"
7325 || strType == "vrdp" /* Deprecated. Same as headless. */
7326 )
7327 {
7328 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7329 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7330 * and a VM works even if the server has not been installed.
7331 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7332 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7333 * differently in 4.0 and 3.x.
7334 */
7335 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7336 Assert(sz >= sizeof(VBoxHeadless_exe));
7337 strcpy(cmd, VBoxHeadless_exe);
7338
7339 Utf8Str idStr = mData->mUuid.toString();
7340 /* Leave space for "--capture" arg. */
7341 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7342 "--startvm", idStr.c_str(),
7343 "--vrde", "config",
7344 0, /* For "--capture". */
7345 0 };
7346 if (strType == "capture")
7347 {
7348 unsigned pos = RT_ELEMENTS(args) - 2;
7349 args[pos] = "--capture";
7350 }
7351 vrc = RTProcCreate(szPath, args, env,
7352#ifdef RT_OS_WINDOWS
7353 RTPROC_FLAGS_NO_WINDOW
7354#else
7355 0
7356#endif
7357 , &pid);
7358 }
7359#else /* !VBOX_WITH_HEADLESS */
7360 if (0)
7361 ;
7362#endif /* !VBOX_WITH_HEADLESS */
7363 else
7364 {
7365 RTEnvDestroy(env);
7366 return setError(E_INVALIDARG,
7367 tr("Invalid session type: '%s'"),
7368 strType.c_str());
7369 }
7370
7371 RTEnvDestroy(env);
7372
7373 if (RT_FAILURE(vrc))
7374 return setError(VBOX_E_IPRT_ERROR,
7375 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7376 mUserData->s.strName.c_str(), vrc);
7377
7378 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7379
7380 /*
7381 * Note that we don't release the lock here before calling the client,
7382 * because it doesn't need to call us back if called with a NULL argument.
7383 * Releasing the lock here is dangerous because we didn't prepare the
7384 * launch data yet, but the client we've just started may happen to be
7385 * too fast and call openSession() that will fail (because of PID, etc.),
7386 * so that the Machine will never get out of the Spawning session state.
7387 */
7388
7389 /* inform the session that it will be a remote one */
7390 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7391 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7392 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7393
7394 if (FAILED(rc))
7395 {
7396 /* restore the session state */
7397 mData->mSession.mState = SessionState_Unlocked;
7398 /* The failure may occur w/o any error info (from RPC), so provide one */
7399 return setError(VBOX_E_VM_ERROR,
7400 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7401 }
7402
7403 /* attach launch data to the machine */
7404 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7405 mData->mSession.mRemoteControls.push_back(aControl);
7406 mData->mSession.mProgress = aProgress;
7407 mData->mSession.mPID = pid;
7408 mData->mSession.mState = SessionState_Spawning;
7409 mData->mSession.mType = strType;
7410
7411 LogFlowThisFuncLeave();
7412 return S_OK;
7413}
7414
7415/**
7416 * Returns @c true if the given machine has an open direct session and returns
7417 * the session machine instance and additional session data (on some platforms)
7418 * if so.
7419 *
7420 * Note that when the method returns @c false, the arguments remain unchanged.
7421 *
7422 * @param aMachine Session machine object.
7423 * @param aControl Direct session control object (optional).
7424 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7425 *
7426 * @note locks this object for reading.
7427 */
7428#if defined(RT_OS_WINDOWS)
7429bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7430 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7431 HANDLE *aIPCSem /*= NULL*/,
7432 bool aAllowClosing /*= false*/)
7433#elif defined(RT_OS_OS2)
7434bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7435 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7436 HMTX *aIPCSem /*= NULL*/,
7437 bool aAllowClosing /*= false*/)
7438#else
7439bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7440 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7441 bool aAllowClosing /*= false*/)
7442#endif
7443{
7444 AutoLimitedCaller autoCaller(this);
7445 AssertComRCReturn(autoCaller.rc(), false);
7446
7447 /* just return false for inaccessible machines */
7448 if (autoCaller.state() != Ready)
7449 return false;
7450
7451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 if ( mData->mSession.mState == SessionState_Locked
7454 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7455 )
7456 {
7457 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7458
7459 aMachine = mData->mSession.mMachine;
7460
7461 if (aControl != NULL)
7462 *aControl = mData->mSession.mDirectControl;
7463
7464#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7465 /* Additional session data */
7466 if (aIPCSem != NULL)
7467 *aIPCSem = aMachine->mIPCSem;
7468#endif
7469 return true;
7470 }
7471
7472 return false;
7473}
7474
7475/**
7476 * Returns @c true if the given machine has an spawning direct session and
7477 * returns and additional session data (on some platforms) if so.
7478 *
7479 * Note that when the method returns @c false, the arguments remain unchanged.
7480 *
7481 * @param aPID PID of the spawned direct session process.
7482 *
7483 * @note locks this object for reading.
7484 */
7485#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7486bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7487#else
7488bool Machine::isSessionSpawning()
7489#endif
7490{
7491 AutoLimitedCaller autoCaller(this);
7492 AssertComRCReturn(autoCaller.rc(), false);
7493
7494 /* just return false for inaccessible machines */
7495 if (autoCaller.state() != Ready)
7496 return false;
7497
7498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7499
7500 if (mData->mSession.mState == SessionState_Spawning)
7501 {
7502#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7503 /* Additional session data */
7504 if (aPID != NULL)
7505 {
7506 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7507 *aPID = mData->mSession.mPID;
7508 }
7509#endif
7510 return true;
7511 }
7512
7513 return false;
7514}
7515
7516/**
7517 * Called from the client watcher thread to check for unexpected client process
7518 * death during Session_Spawning state (e.g. before it successfully opened a
7519 * direct session).
7520 *
7521 * On Win32 and on OS/2, this method is called only when we've got the
7522 * direct client's process termination notification, so it always returns @c
7523 * true.
7524 *
7525 * On other platforms, this method returns @c true if the client process is
7526 * terminated and @c false if it's still alive.
7527 *
7528 * @note Locks this object for writing.
7529 */
7530bool Machine::checkForSpawnFailure()
7531{
7532 AutoCaller autoCaller(this);
7533 if (!autoCaller.isOk())
7534 {
7535 /* nothing to do */
7536 LogFlowThisFunc(("Already uninitialized!\n"));
7537 return true;
7538 }
7539
7540 /* VirtualBox::addProcessToReap() needs a write lock */
7541 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7542
7543 if (mData->mSession.mState != SessionState_Spawning)
7544 {
7545 /* nothing to do */
7546 LogFlowThisFunc(("Not spawning any more!\n"));
7547 return true;
7548 }
7549
7550 HRESULT rc = S_OK;
7551
7552#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7553
7554 /* the process was already unexpectedly terminated, we just need to set an
7555 * error and finalize session spawning */
7556 rc = setError(E_FAIL,
7557 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7558 getName().c_str());
7559#else
7560
7561 /* PID not yet initialized, skip check. */
7562 if (mData->mSession.mPID == NIL_RTPROCESS)
7563 return false;
7564
7565 RTPROCSTATUS status;
7566 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7567 &status);
7568
7569 if (vrc != VERR_PROCESS_RUNNING)
7570 {
7571 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7572 rc = setError(E_FAIL,
7573 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7574 getName().c_str(), status.iStatus);
7575 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7576 rc = setError(E_FAIL,
7577 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7578 getName().c_str(), status.iStatus);
7579 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7580 rc = setError(E_FAIL,
7581 tr("The virtual machine '%s' has terminated abnormally"),
7582 getName().c_str(), status.iStatus);
7583 else
7584 rc = setError(E_FAIL,
7585 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7586 getName().c_str(), rc);
7587 }
7588
7589#endif
7590
7591 if (FAILED(rc))
7592 {
7593 /* Close the remote session, remove the remote control from the list
7594 * and reset session state to Closed (@note keep the code in sync with
7595 * the relevant part in checkForSpawnFailure()). */
7596
7597 Assert(mData->mSession.mRemoteControls.size() == 1);
7598 if (mData->mSession.mRemoteControls.size() == 1)
7599 {
7600 ErrorInfoKeeper eik;
7601 mData->mSession.mRemoteControls.front()->Uninitialize();
7602 }
7603
7604 mData->mSession.mRemoteControls.clear();
7605 mData->mSession.mState = SessionState_Unlocked;
7606
7607 /* finalize the progress after setting the state */
7608 if (!mData->mSession.mProgress.isNull())
7609 {
7610 mData->mSession.mProgress->notifyComplete(rc);
7611 mData->mSession.mProgress.setNull();
7612 }
7613
7614 mParent->addProcessToReap(mData->mSession.mPID);
7615 mData->mSession.mPID = NIL_RTPROCESS;
7616
7617 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7618 return true;
7619 }
7620
7621 return false;
7622}
7623
7624/**
7625 * Checks whether the machine can be registered. If so, commits and saves
7626 * all settings.
7627 *
7628 * @note Must be called from mParent's write lock. Locks this object and
7629 * children for writing.
7630 */
7631HRESULT Machine::prepareRegister()
7632{
7633 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7634
7635 AutoLimitedCaller autoCaller(this);
7636 AssertComRCReturnRC(autoCaller.rc());
7637
7638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7639
7640 /* wait for state dependents to drop to zero */
7641 ensureNoStateDependencies();
7642
7643 if (!mData->mAccessible)
7644 return setError(VBOX_E_INVALID_OBJECT_STATE,
7645 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7646 mUserData->s.strName.c_str(),
7647 mData->mUuid.toString().c_str());
7648
7649 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7650
7651 if (mData->mRegistered)
7652 return setError(VBOX_E_INVALID_OBJECT_STATE,
7653 tr("The machine '%s' with UUID {%s} is already registered"),
7654 mUserData->s.strName.c_str(),
7655 mData->mUuid.toString().c_str());
7656
7657 HRESULT rc = S_OK;
7658
7659 // Ensure the settings are saved. If we are going to be registered and
7660 // no config file exists yet, create it by calling saveSettings() too.
7661 if ( (mData->flModifications)
7662 || (!mData->pMachineConfigFile->fileExists())
7663 )
7664 {
7665 rc = saveSettings(NULL);
7666 // no need to check whether VirtualBox.xml needs saving too since
7667 // we can't have a machine XML file rename pending
7668 if (FAILED(rc)) return rc;
7669 }
7670
7671 /* more config checking goes here */
7672
7673 if (SUCCEEDED(rc))
7674 {
7675 /* we may have had implicit modifications we want to fix on success */
7676 commit();
7677
7678 mData->mRegistered = true;
7679 }
7680 else
7681 {
7682 /* we may have had implicit modifications we want to cancel on failure*/
7683 rollback(false /* aNotify */);
7684 }
7685
7686 return rc;
7687}
7688
7689/**
7690 * Increases the number of objects dependent on the machine state or on the
7691 * registered state. Guarantees that these two states will not change at least
7692 * until #releaseStateDependency() is called.
7693 *
7694 * Depending on the @a aDepType value, additional state checks may be made.
7695 * These checks will set extended error info on failure. See
7696 * #checkStateDependency() for more info.
7697 *
7698 * If this method returns a failure, the dependency is not added and the caller
7699 * is not allowed to rely on any particular machine state or registration state
7700 * value and may return the failed result code to the upper level.
7701 *
7702 * @param aDepType Dependency type to add.
7703 * @param aState Current machine state (NULL if not interested).
7704 * @param aRegistered Current registered state (NULL if not interested).
7705 *
7706 * @note Locks this object for writing.
7707 */
7708HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7709 MachineState_T *aState /* = NULL */,
7710 BOOL *aRegistered /* = NULL */)
7711{
7712 AutoCaller autoCaller(this);
7713 AssertComRCReturnRC(autoCaller.rc());
7714
7715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7716
7717 HRESULT rc = checkStateDependency(aDepType);
7718 if (FAILED(rc)) return rc;
7719
7720 {
7721 if (mData->mMachineStateChangePending != 0)
7722 {
7723 /* ensureNoStateDependencies() is waiting for state dependencies to
7724 * drop to zero so don't add more. It may make sense to wait a bit
7725 * and retry before reporting an error (since the pending state
7726 * transition should be really quick) but let's just assert for
7727 * now to see if it ever happens on practice. */
7728
7729 AssertFailed();
7730
7731 return setError(E_ACCESSDENIED,
7732 tr("Machine state change is in progress. Please retry the operation later."));
7733 }
7734
7735 ++mData->mMachineStateDeps;
7736 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7737 }
7738
7739 if (aState)
7740 *aState = mData->mMachineState;
7741 if (aRegistered)
7742 *aRegistered = mData->mRegistered;
7743
7744 return S_OK;
7745}
7746
7747/**
7748 * Decreases the number of objects dependent on the machine state.
7749 * Must always complete the #addStateDependency() call after the state
7750 * dependency is no more necessary.
7751 */
7752void Machine::releaseStateDependency()
7753{
7754 AutoCaller autoCaller(this);
7755 AssertComRCReturnVoid(autoCaller.rc());
7756
7757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7758
7759 /* releaseStateDependency() w/o addStateDependency()? */
7760 AssertReturnVoid(mData->mMachineStateDeps != 0);
7761 -- mData->mMachineStateDeps;
7762
7763 if (mData->mMachineStateDeps == 0)
7764 {
7765 /* inform ensureNoStateDependencies() that there are no more deps */
7766 if (mData->mMachineStateChangePending != 0)
7767 {
7768 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7769 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7770 }
7771 }
7772}
7773
7774// protected methods
7775/////////////////////////////////////////////////////////////////////////////
7776
7777/**
7778 * Performs machine state checks based on the @a aDepType value. If a check
7779 * fails, this method will set extended error info, otherwise it will return
7780 * S_OK. It is supposed, that on failure, the caller will immediately return
7781 * the return value of this method to the upper level.
7782 *
7783 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7784 *
7785 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7786 * current state of this machine object allows to change settings of the
7787 * machine (i.e. the machine is not registered, or registered but not running
7788 * and not saved). It is useful to call this method from Machine setters
7789 * before performing any change.
7790 *
7791 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7792 * as for MutableStateDep except that if the machine is saved, S_OK is also
7793 * returned. This is useful in setters which allow changing machine
7794 * properties when it is in the saved state.
7795 *
7796 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7797 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7798 * Aborted).
7799 *
7800 * @param aDepType Dependency type to check.
7801 *
7802 * @note Non Machine based classes should use #addStateDependency() and
7803 * #releaseStateDependency() methods or the smart AutoStateDependency
7804 * template.
7805 *
7806 * @note This method must be called from under this object's read or write
7807 * lock.
7808 */
7809HRESULT Machine::checkStateDependency(StateDependency aDepType)
7810{
7811 switch (aDepType)
7812 {
7813 case AnyStateDep:
7814 {
7815 break;
7816 }
7817 case MutableStateDep:
7818 {
7819 if ( mData->mRegistered
7820 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7821 || ( mData->mMachineState != MachineState_Paused
7822 && mData->mMachineState != MachineState_Running
7823 && mData->mMachineState != MachineState_Aborted
7824 && mData->mMachineState != MachineState_Teleported
7825 && mData->mMachineState != MachineState_PoweredOff
7826 )
7827 )
7828 )
7829 return setError(VBOX_E_INVALID_VM_STATE,
7830 tr("The machine is not mutable (state is %s)"),
7831 Global::stringifyMachineState(mData->mMachineState));
7832 break;
7833 }
7834 case MutableOrSavedStateDep:
7835 {
7836 if ( mData->mRegistered
7837 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7838 || ( mData->mMachineState != MachineState_Paused
7839 && mData->mMachineState != MachineState_Running
7840 && mData->mMachineState != MachineState_Aborted
7841 && mData->mMachineState != MachineState_Teleported
7842 && mData->mMachineState != MachineState_Saved
7843 && mData->mMachineState != MachineState_PoweredOff
7844 )
7845 )
7846 )
7847 return setError(VBOX_E_INVALID_VM_STATE,
7848 tr("The machine is not mutable (state is %s)"),
7849 Global::stringifyMachineState(mData->mMachineState));
7850 break;
7851 }
7852 case OfflineStateDep:
7853 {
7854 if ( mData->mRegistered
7855 && ( !isSessionMachine()
7856 || ( mData->mMachineState != MachineState_PoweredOff
7857 && mData->mMachineState != MachineState_Saved
7858 && mData->mMachineState != MachineState_Aborted
7859 && mData->mMachineState != MachineState_Teleported
7860 )
7861 )
7862 )
7863 return setError(VBOX_E_INVALID_VM_STATE,
7864 tr("The machine is not offline (state is %s)"),
7865 Global::stringifyMachineState(mData->mMachineState));
7866 break;
7867 }
7868 }
7869
7870 return S_OK;
7871}
7872
7873/**
7874 * Helper to initialize all associated child objects and allocate data
7875 * structures.
7876 *
7877 * This method must be called as a part of the object's initialization procedure
7878 * (usually done in the #init() method).
7879 *
7880 * @note Must be called only from #init() or from #registeredInit().
7881 */
7882HRESULT Machine::initDataAndChildObjects()
7883{
7884 AutoCaller autoCaller(this);
7885 AssertComRCReturnRC(autoCaller.rc());
7886 AssertComRCReturn(autoCaller.state() == InInit ||
7887 autoCaller.state() == Limited, E_FAIL);
7888
7889 AssertReturn(!mData->mAccessible, E_FAIL);
7890
7891 /* allocate data structures */
7892 mSSData.allocate();
7893 mUserData.allocate();
7894 mHWData.allocate();
7895 mMediaData.allocate();
7896 mStorageControllers.allocate();
7897
7898 /* initialize mOSTypeId */
7899 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7900
7901 /* create associated BIOS settings object */
7902 unconst(mBIOSSettings).createObject();
7903 mBIOSSettings->init(this);
7904
7905 /* create an associated VRDE object (default is disabled) */
7906 unconst(mVRDEServer).createObject();
7907 mVRDEServer->init(this);
7908
7909 /* create associated serial port objects */
7910 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7911 {
7912 unconst(mSerialPorts[slot]).createObject();
7913 mSerialPorts[slot]->init(this, slot);
7914 }
7915
7916 /* create associated parallel port objects */
7917 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7918 {
7919 unconst(mParallelPorts[slot]).createObject();
7920 mParallelPorts[slot]->init(this, slot);
7921 }
7922
7923 /* create the audio adapter object (always present, default is disabled) */
7924 unconst(mAudioAdapter).createObject();
7925 mAudioAdapter->init(this);
7926
7927 /* create the USB controller object (always present, default is disabled) */
7928 unconst(mUSBController).createObject();
7929 mUSBController->init(this);
7930
7931 /* create associated network adapter objects */
7932 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7933 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7934 {
7935 unconst(mNetworkAdapters[slot]).createObject();
7936 mNetworkAdapters[slot]->init(this, slot);
7937 }
7938
7939 /* create the bandwidth control */
7940 unconst(mBandwidthControl).createObject();
7941 mBandwidthControl->init(this);
7942
7943 return S_OK;
7944}
7945
7946/**
7947 * Helper to uninitialize all associated child objects and to free all data
7948 * structures.
7949 *
7950 * This method must be called as a part of the object's uninitialization
7951 * procedure (usually done in the #uninit() method).
7952 *
7953 * @note Must be called only from #uninit() or from #registeredInit().
7954 */
7955void Machine::uninitDataAndChildObjects()
7956{
7957 AutoCaller autoCaller(this);
7958 AssertComRCReturnVoid(autoCaller.rc());
7959 AssertComRCReturnVoid( autoCaller.state() == InUninit
7960 || autoCaller.state() == Limited);
7961
7962 /* tell all our other child objects we've been uninitialized */
7963 if (mBandwidthControl)
7964 {
7965 mBandwidthControl->uninit();
7966 unconst(mBandwidthControl).setNull();
7967 }
7968
7969 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7970 {
7971 if (mNetworkAdapters[slot])
7972 {
7973 mNetworkAdapters[slot]->uninit();
7974 unconst(mNetworkAdapters[slot]).setNull();
7975 }
7976 }
7977
7978 if (mUSBController)
7979 {
7980 mUSBController->uninit();
7981 unconst(mUSBController).setNull();
7982 }
7983
7984 if (mAudioAdapter)
7985 {
7986 mAudioAdapter->uninit();
7987 unconst(mAudioAdapter).setNull();
7988 }
7989
7990 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7991 {
7992 if (mParallelPorts[slot])
7993 {
7994 mParallelPorts[slot]->uninit();
7995 unconst(mParallelPorts[slot]).setNull();
7996 }
7997 }
7998
7999 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8000 {
8001 if (mSerialPorts[slot])
8002 {
8003 mSerialPorts[slot]->uninit();
8004 unconst(mSerialPorts[slot]).setNull();
8005 }
8006 }
8007
8008 if (mVRDEServer)
8009 {
8010 mVRDEServer->uninit();
8011 unconst(mVRDEServer).setNull();
8012 }
8013
8014 if (mBIOSSettings)
8015 {
8016 mBIOSSettings->uninit();
8017 unconst(mBIOSSettings).setNull();
8018 }
8019
8020 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
8021 * instance is uninitialized; SessionMachine instances refer to real
8022 * Machine hard disks). This is necessary for a clean re-initialization of
8023 * the VM after successfully re-checking the accessibility state. Note
8024 * that in case of normal Machine or SnapshotMachine uninitialization (as
8025 * a result of unregistering or deleting the snapshot), outdated hard
8026 * disk attachments will already be uninitialized and deleted, so this
8027 * code will not affect them. */
8028 if ( !!mMediaData
8029 && (!isSessionMachine())
8030 )
8031 {
8032 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8033 it != mMediaData->mAttachments.end();
8034 ++it)
8035 {
8036 ComObjPtr<Medium> hd = (*it)->getMedium();
8037 if (hd.isNull())
8038 continue;
8039 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
8040 AssertComRC(rc);
8041 }
8042 }
8043
8044 if (!isSessionMachine() && !isSnapshotMachine())
8045 {
8046 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8047 if (mData->mFirstSnapshot)
8048 {
8049 // snapshots tree is protected by machine write lock; strictly
8050 // this isn't necessary here since we're deleting the entire
8051 // machine, but otherwise we assert in Snapshot::uninit()
8052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8053 mData->mFirstSnapshot->uninit();
8054 mData->mFirstSnapshot.setNull();
8055 }
8056
8057 mData->mCurrentSnapshot.setNull();
8058 }
8059
8060 /* free data structures (the essential mData structure is not freed here
8061 * since it may be still in use) */
8062 mMediaData.free();
8063 mStorageControllers.free();
8064 mHWData.free();
8065 mUserData.free();
8066 mSSData.free();
8067}
8068
8069/**
8070 * Returns a pointer to the Machine object for this machine that acts like a
8071 * parent for complex machine data objects such as shared folders, etc.
8072 *
8073 * For primary Machine objects and for SnapshotMachine objects, returns this
8074 * object's pointer itself. For SessionMachine objects, returns the peer
8075 * (primary) machine pointer.
8076 */
8077Machine* Machine::getMachine()
8078{
8079 if (isSessionMachine())
8080 return (Machine*)mPeer;
8081 return this;
8082}
8083
8084/**
8085 * Makes sure that there are no machine state dependents. If necessary, waits
8086 * for the number of dependents to drop to zero.
8087 *
8088 * Make sure this method is called from under this object's write lock to
8089 * guarantee that no new dependents may be added when this method returns
8090 * control to the caller.
8091 *
8092 * @note Locks this object for writing. The lock will be released while waiting
8093 * (if necessary).
8094 *
8095 * @warning To be used only in methods that change the machine state!
8096 */
8097void Machine::ensureNoStateDependencies()
8098{
8099 AssertReturnVoid(isWriteLockOnCurrentThread());
8100
8101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8102
8103 /* Wait for all state dependents if necessary */
8104 if (mData->mMachineStateDeps != 0)
8105 {
8106 /* lazy semaphore creation */
8107 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8108 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8109
8110 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8111 mData->mMachineStateDeps));
8112
8113 ++mData->mMachineStateChangePending;
8114
8115 /* reset the semaphore before waiting, the last dependent will signal
8116 * it */
8117 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8118
8119 alock.release();
8120
8121 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8122
8123 alock.acquire();
8124
8125 -- mData->mMachineStateChangePending;
8126 }
8127}
8128
8129/**
8130 * Changes the machine state and informs callbacks.
8131 *
8132 * This method is not intended to fail so it either returns S_OK or asserts (and
8133 * returns a failure).
8134 *
8135 * @note Locks this object for writing.
8136 */
8137HRESULT Machine::setMachineState(MachineState_T aMachineState)
8138{
8139 LogFlowThisFuncEnter();
8140 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8141
8142 AutoCaller autoCaller(this);
8143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8144
8145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8146
8147 /* wait for state dependents to drop to zero */
8148 ensureNoStateDependencies();
8149
8150 if (mData->mMachineState != aMachineState)
8151 {
8152 mData->mMachineState = aMachineState;
8153
8154 RTTimeNow(&mData->mLastStateChange);
8155
8156 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8157 }
8158
8159 LogFlowThisFuncLeave();
8160 return S_OK;
8161}
8162
8163/**
8164 * Searches for a shared folder with the given logical name
8165 * in the collection of shared folders.
8166 *
8167 * @param aName logical name of the shared folder
8168 * @param aSharedFolder where to return the found object
8169 * @param aSetError whether to set the error info if the folder is
8170 * not found
8171 * @return
8172 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8173 *
8174 * @note
8175 * must be called from under the object's lock!
8176 */
8177HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8178 ComObjPtr<SharedFolder> &aSharedFolder,
8179 bool aSetError /* = false */)
8180{
8181 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8182 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8183 it != mHWData->mSharedFolders.end();
8184 ++it)
8185 {
8186 SharedFolder *pSF = *it;
8187 AutoCaller autoCaller(pSF);
8188 if (pSF->getName() == aName)
8189 {
8190 aSharedFolder = pSF;
8191 rc = S_OK;
8192 break;
8193 }
8194 }
8195
8196 if (aSetError && FAILED(rc))
8197 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8198
8199 return rc;
8200}
8201
8202/**
8203 * Initializes all machine instance data from the given settings structures
8204 * from XML. The exception is the machine UUID which needs special handling
8205 * depending on the caller's use case, so the caller needs to set that herself.
8206 *
8207 * This gets called in several contexts during machine initialization:
8208 *
8209 * -- When machine XML exists on disk already and needs to be loaded into memory,
8210 * for example, from registeredInit() to load all registered machines on
8211 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8212 * attached to the machine should be part of some media registry already.
8213 *
8214 * -- During OVF import, when a machine config has been constructed from an
8215 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8216 * ensure that the media listed as attachments in the config (which have
8217 * been imported from the OVF) receive the correct registry ID.
8218 *
8219 * -- During VM cloning.
8220 *
8221 * @param config Machine settings from XML.
8222 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8223 * @return
8224 */
8225HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8226 const Guid *puuidRegistry)
8227{
8228 // copy name, description, OS type, teleporter, UTC etc.
8229 mUserData->s = config.machineUserData;
8230
8231 // look up the object by Id to check it is valid
8232 ComPtr<IGuestOSType> guestOSType;
8233 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8234 guestOSType.asOutParam());
8235 if (FAILED(rc)) return rc;
8236
8237 // stateFile (optional)
8238 if (config.strStateFile.isEmpty())
8239 mSSData->strStateFilePath.setNull();
8240 else
8241 {
8242 Utf8Str stateFilePathFull(config.strStateFile);
8243 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8244 if (RT_FAILURE(vrc))
8245 return setError(E_FAIL,
8246 tr("Invalid saved state file path '%s' (%Rrc)"),
8247 config.strStateFile.c_str(),
8248 vrc);
8249 mSSData->strStateFilePath = stateFilePathFull;
8250 }
8251
8252 // snapshot folder needs special processing so set it again
8253 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8254 if (FAILED(rc)) return rc;
8255
8256 /* Copy the extra data items (Not in any case config is already the same as
8257 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8258 * make sure the extra data map is copied). */
8259 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8260
8261 /* currentStateModified (optional, default is true) */
8262 mData->mCurrentStateModified = config.fCurrentStateModified;
8263
8264 mData->mLastStateChange = config.timeLastStateChange;
8265
8266 /*
8267 * note: all mUserData members must be assigned prior this point because
8268 * we need to commit changes in order to let mUserData be shared by all
8269 * snapshot machine instances.
8270 */
8271 mUserData.commitCopy();
8272
8273 // machine registry, if present (must be loaded before snapshots)
8274 if (config.canHaveOwnMediaRegistry())
8275 {
8276 // determine machine folder
8277 Utf8Str strMachineFolder = getSettingsFileFull();
8278 strMachineFolder.stripFilename();
8279 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8280 config.mediaRegistry,
8281 strMachineFolder);
8282 if (FAILED(rc)) return rc;
8283 }
8284
8285 /* Snapshot node (optional) */
8286 size_t cRootSnapshots;
8287 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8288 {
8289 // there must be only one root snapshot
8290 Assert(cRootSnapshots == 1);
8291
8292 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8293
8294 rc = loadSnapshot(snap,
8295 config.uuidCurrentSnapshot,
8296 NULL); // no parent == first snapshot
8297 if (FAILED(rc)) return rc;
8298 }
8299
8300 // hardware data
8301 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8302 if (FAILED(rc)) return rc;
8303
8304 // load storage controllers
8305 rc = loadStorageControllers(config.storageMachine,
8306 puuidRegistry,
8307 NULL /* puuidSnapshot */);
8308 if (FAILED(rc)) return rc;
8309
8310 /*
8311 * NOTE: the assignment below must be the last thing to do,
8312 * otherwise it will be not possible to change the settings
8313 * somewhere in the code above because all setters will be
8314 * blocked by checkStateDependency(MutableStateDep).
8315 */
8316
8317 /* set the machine state to Aborted or Saved when appropriate */
8318 if (config.fAborted)
8319 {
8320 mSSData->strStateFilePath.setNull();
8321
8322 /* no need to use setMachineState() during init() */
8323 mData->mMachineState = MachineState_Aborted;
8324 }
8325 else if (!mSSData->strStateFilePath.isEmpty())
8326 {
8327 /* no need to use setMachineState() during init() */
8328 mData->mMachineState = MachineState_Saved;
8329 }
8330
8331 // after loading settings, we are no longer different from the XML on disk
8332 mData->flModifications = 0;
8333
8334 return S_OK;
8335}
8336
8337/**
8338 * Recursively loads all snapshots starting from the given.
8339 *
8340 * @param aNode <Snapshot> node.
8341 * @param aCurSnapshotId Current snapshot ID from the settings file.
8342 * @param aParentSnapshot Parent snapshot.
8343 */
8344HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8345 const Guid &aCurSnapshotId,
8346 Snapshot *aParentSnapshot)
8347{
8348 AssertReturn(!isSnapshotMachine(), E_FAIL);
8349 AssertReturn(!isSessionMachine(), E_FAIL);
8350
8351 HRESULT rc = S_OK;
8352
8353 Utf8Str strStateFile;
8354 if (!data.strStateFile.isEmpty())
8355 {
8356 /* optional */
8357 strStateFile = data.strStateFile;
8358 int vrc = calculateFullPath(strStateFile, strStateFile);
8359 if (RT_FAILURE(vrc))
8360 return setError(E_FAIL,
8361 tr("Invalid saved state file path '%s' (%Rrc)"),
8362 strStateFile.c_str(),
8363 vrc);
8364 }
8365
8366 /* create a snapshot machine object */
8367 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8368 pSnapshotMachine.createObject();
8369 rc = pSnapshotMachine->initFromSettings(this,
8370 data.hardware,
8371 &data.debugging,
8372 &data.autostart,
8373 data.storage,
8374 data.uuid.ref(),
8375 strStateFile);
8376 if (FAILED(rc)) return rc;
8377
8378 /* create a snapshot object */
8379 ComObjPtr<Snapshot> pSnapshot;
8380 pSnapshot.createObject();
8381 /* initialize the snapshot */
8382 rc = pSnapshot->init(mParent, // VirtualBox object
8383 data.uuid,
8384 data.strName,
8385 data.strDescription,
8386 data.timestamp,
8387 pSnapshotMachine,
8388 aParentSnapshot);
8389 if (FAILED(rc)) return rc;
8390
8391 /* memorize the first snapshot if necessary */
8392 if (!mData->mFirstSnapshot)
8393 mData->mFirstSnapshot = pSnapshot;
8394
8395 /* memorize the current snapshot when appropriate */
8396 if ( !mData->mCurrentSnapshot
8397 && pSnapshot->getId() == aCurSnapshotId
8398 )
8399 mData->mCurrentSnapshot = pSnapshot;
8400
8401 // now create the children
8402 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8403 it != data.llChildSnapshots.end();
8404 ++it)
8405 {
8406 const settings::Snapshot &childData = *it;
8407 // recurse
8408 rc = loadSnapshot(childData,
8409 aCurSnapshotId,
8410 pSnapshot); // parent = the one we created above
8411 if (FAILED(rc)) return rc;
8412 }
8413
8414 return rc;
8415}
8416
8417/**
8418 * Loads settings into mHWData.
8419 *
8420 * @param data Reference to the hardware settings.
8421 * @param pDbg Pointer to the debugging settings.
8422 * @param pAutostart Pointer to the autostart settings.
8423 */
8424HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8425 const settings::Autostart *pAutostart)
8426{
8427 AssertReturn(!isSessionMachine(), E_FAIL);
8428
8429 HRESULT rc = S_OK;
8430
8431 try
8432 {
8433 /* The hardware version attribute (optional). */
8434 mHWData->mHWVersion = data.strVersion;
8435 mHWData->mHardwareUUID = data.uuid;
8436
8437 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8438 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8439 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8440 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8441 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8442 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8443 mHWData->mPAEEnabled = data.fPAE;
8444 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8445
8446 mHWData->mCPUCount = data.cCPUs;
8447 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8448 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8449
8450 // cpu
8451 if (mHWData->mCPUHotPlugEnabled)
8452 {
8453 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8454 it != data.llCpus.end();
8455 ++it)
8456 {
8457 const settings::Cpu &cpu = *it;
8458
8459 mHWData->mCPUAttached[cpu.ulId] = true;
8460 }
8461 }
8462
8463 // cpuid leafs
8464 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8465 it != data.llCpuIdLeafs.end();
8466 ++it)
8467 {
8468 const settings::CpuIdLeaf &leaf = *it;
8469
8470 switch (leaf.ulId)
8471 {
8472 case 0x0:
8473 case 0x1:
8474 case 0x2:
8475 case 0x3:
8476 case 0x4:
8477 case 0x5:
8478 case 0x6:
8479 case 0x7:
8480 case 0x8:
8481 case 0x9:
8482 case 0xA:
8483 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8484 break;
8485
8486 case 0x80000000:
8487 case 0x80000001:
8488 case 0x80000002:
8489 case 0x80000003:
8490 case 0x80000004:
8491 case 0x80000005:
8492 case 0x80000006:
8493 case 0x80000007:
8494 case 0x80000008:
8495 case 0x80000009:
8496 case 0x8000000A:
8497 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8498 break;
8499
8500 default:
8501 /* just ignore */
8502 break;
8503 }
8504 }
8505
8506 mHWData->mMemorySize = data.ulMemorySizeMB;
8507 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8508
8509 // boot order
8510 for (size_t i = 0;
8511 i < RT_ELEMENTS(mHWData->mBootOrder);
8512 i++)
8513 {
8514 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8515 if (it == data.mapBootOrder.end())
8516 mHWData->mBootOrder[i] = DeviceType_Null;
8517 else
8518 mHWData->mBootOrder[i] = it->second;
8519 }
8520
8521 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8522 mHWData->mMonitorCount = data.cMonitors;
8523 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8524 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8525 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8526 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8527 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8528 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8529 mHWData->mFirmwareType = data.firmwareType;
8530 mHWData->mPointingHIDType = data.pointingHIDType;
8531 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8532 mHWData->mChipsetType = data.chipsetType;
8533 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8534 mHWData->mHPETEnabled = data.fHPETEnabled;
8535
8536 /* VRDEServer */
8537 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8538 if (FAILED(rc)) return rc;
8539
8540 /* BIOS */
8541 rc = mBIOSSettings->loadSettings(data.biosSettings);
8542 if (FAILED(rc)) return rc;
8543
8544 // Bandwidth control (must come before network adapters)
8545 rc = mBandwidthControl->loadSettings(data.ioSettings);
8546 if (FAILED(rc)) return rc;
8547
8548 /* USB Controller */
8549 rc = mUSBController->loadSettings(data.usbController);
8550 if (FAILED(rc)) return rc;
8551
8552 // network adapters
8553 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8554 uint32_t oldCount = mNetworkAdapters.size();
8555 if (newCount > oldCount)
8556 {
8557 mNetworkAdapters.resize(newCount);
8558 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8559 {
8560 unconst(mNetworkAdapters[slot]).createObject();
8561 mNetworkAdapters[slot]->init(this, slot);
8562 }
8563 }
8564 else if (newCount < oldCount)
8565 mNetworkAdapters.resize(newCount);
8566 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8567 it != data.llNetworkAdapters.end();
8568 ++it)
8569 {
8570 const settings::NetworkAdapter &nic = *it;
8571
8572 /* slot unicity is guaranteed by XML Schema */
8573 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8574 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8575 if (FAILED(rc)) return rc;
8576 }
8577
8578 // serial ports
8579 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8580 it != data.llSerialPorts.end();
8581 ++it)
8582 {
8583 const settings::SerialPort &s = *it;
8584
8585 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8586 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8587 if (FAILED(rc)) return rc;
8588 }
8589
8590 // parallel ports (optional)
8591 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8592 it != data.llParallelPorts.end();
8593 ++it)
8594 {
8595 const settings::ParallelPort &p = *it;
8596
8597 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8598 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8599 if (FAILED(rc)) return rc;
8600 }
8601
8602 /* AudioAdapter */
8603 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8604 if (FAILED(rc)) return rc;
8605
8606 /* Shared folders */
8607 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8608 it != data.llSharedFolders.end();
8609 ++it)
8610 {
8611 const settings::SharedFolder &sf = *it;
8612
8613 ComObjPtr<SharedFolder> sharedFolder;
8614 /* Check for double entries. Not allowed! */
8615 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8616 if (SUCCEEDED(rc))
8617 return setError(VBOX_E_OBJECT_IN_USE,
8618 tr("Shared folder named '%s' already exists"),
8619 sf.strName.c_str());
8620
8621 /* Create the new shared folder. Don't break on error. This will be
8622 * reported when the machine starts. */
8623 sharedFolder.createObject();
8624 rc = sharedFolder->init(getMachine(),
8625 sf.strName,
8626 sf.strHostPath,
8627 RT_BOOL(sf.fWritable),
8628 RT_BOOL(sf.fAutoMount),
8629 false /* fFailOnError */);
8630 if (FAILED(rc)) return rc;
8631 mHWData->mSharedFolders.push_back(sharedFolder);
8632 }
8633
8634 // Clipboard
8635 mHWData->mClipboardMode = data.clipboardMode;
8636
8637 // drag'n'drop
8638 mHWData->mDragAndDropMode = data.dragAndDropMode;
8639
8640 // guest settings
8641 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8642
8643 // IO settings
8644 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8645 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8646
8647 // Host PCI devices
8648 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8649 it != data.pciAttachments.end();
8650 ++it)
8651 {
8652 const settings::HostPCIDeviceAttachment &hpda = *it;
8653 ComObjPtr<PCIDeviceAttachment> pda;
8654
8655 pda.createObject();
8656 pda->loadSettings(this, hpda);
8657 mHWData->mPCIDeviceAssignments.push_back(pda);
8658 }
8659
8660 /*
8661 * (The following isn't really real hardware, but it lives in HWData
8662 * for reasons of convenience.)
8663 */
8664
8665#ifdef VBOX_WITH_GUEST_PROPS
8666 /* Guest properties (optional) */
8667 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8668 it != data.llGuestProperties.end();
8669 ++it)
8670 {
8671 const settings::GuestProperty &prop = *it;
8672 uint32_t fFlags = guestProp::NILFLAG;
8673 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8674 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8675 mHWData->mGuestProperties.push_back(property);
8676 }
8677
8678 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8679#endif /* VBOX_WITH_GUEST_PROPS defined */
8680
8681 rc = loadDebugging(pDbg);
8682 if (FAILED(rc))
8683 return rc;
8684
8685 mHWData->mAutostart = *pAutostart;
8686 }
8687 catch(std::bad_alloc &)
8688 {
8689 return E_OUTOFMEMORY;
8690 }
8691
8692 AssertComRC(rc);
8693 return rc;
8694}
8695
8696/**
8697 * Called from Machine::loadHardware() to load the debugging settings of the
8698 * machine.
8699 *
8700 * @param pDbg Pointer to the settings.
8701 */
8702HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8703{
8704 mHWData->mDebugging = *pDbg;
8705 /* no more processing currently required, this will probably change. */
8706 return S_OK;
8707}
8708
8709/**
8710 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8711 *
8712 * @param data
8713 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8714 * @param puuidSnapshot
8715 * @return
8716 */
8717HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8718 const Guid *puuidRegistry,
8719 const Guid *puuidSnapshot)
8720{
8721 AssertReturn(!isSessionMachine(), E_FAIL);
8722
8723 HRESULT rc = S_OK;
8724
8725 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8726 it != data.llStorageControllers.end();
8727 ++it)
8728 {
8729 const settings::StorageController &ctlData = *it;
8730
8731 ComObjPtr<StorageController> pCtl;
8732 /* Try to find one with the name first. */
8733 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8734 if (SUCCEEDED(rc))
8735 return setError(VBOX_E_OBJECT_IN_USE,
8736 tr("Storage controller named '%s' already exists"),
8737 ctlData.strName.c_str());
8738
8739 pCtl.createObject();
8740 rc = pCtl->init(this,
8741 ctlData.strName,
8742 ctlData.storageBus,
8743 ctlData.ulInstance,
8744 ctlData.fBootable);
8745 if (FAILED(rc)) return rc;
8746
8747 mStorageControllers->push_back(pCtl);
8748
8749 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8750 if (FAILED(rc)) return rc;
8751
8752 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8753 if (FAILED(rc)) return rc;
8754
8755 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8756 if (FAILED(rc)) return rc;
8757
8758 /* Set IDE emulation settings (only for AHCI controller). */
8759 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8760 {
8761 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8762 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8763 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8764 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8765 )
8766 return rc;
8767 }
8768
8769 /* Load the attached devices now. */
8770 rc = loadStorageDevices(pCtl,
8771 ctlData,
8772 puuidRegistry,
8773 puuidSnapshot);
8774 if (FAILED(rc)) return rc;
8775 }
8776
8777 return S_OK;
8778}
8779
8780/**
8781 * Called from loadStorageControllers for a controller's devices.
8782 *
8783 * @param aStorageController
8784 * @param data
8785 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8786 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8787 * @return
8788 */
8789HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8790 const settings::StorageController &data,
8791 const Guid *puuidRegistry,
8792 const Guid *puuidSnapshot)
8793{
8794 HRESULT rc = S_OK;
8795
8796 /* paranoia: detect duplicate attachments */
8797 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8798 it != data.llAttachedDevices.end();
8799 ++it)
8800 {
8801 const settings::AttachedDevice &ad = *it;
8802
8803 for (settings::AttachedDevicesList::const_iterator it2 = it;
8804 it2 != data.llAttachedDevices.end();
8805 ++it2)
8806 {
8807 if (it == it2)
8808 continue;
8809
8810 const settings::AttachedDevice &ad2 = *it2;
8811
8812 if ( ad.lPort == ad2.lPort
8813 && ad.lDevice == ad2.lDevice)
8814 {
8815 return setError(E_FAIL,
8816 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8817 aStorageController->getName().c_str(),
8818 ad.lPort,
8819 ad.lDevice,
8820 mUserData->s.strName.c_str());
8821 }
8822 }
8823 }
8824
8825 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8826 it != data.llAttachedDevices.end();
8827 ++it)
8828 {
8829 const settings::AttachedDevice &dev = *it;
8830 ComObjPtr<Medium> medium;
8831
8832 switch (dev.deviceType)
8833 {
8834 case DeviceType_Floppy:
8835 case DeviceType_DVD:
8836 if (dev.strHostDriveSrc.isNotEmpty())
8837 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8838 else
8839 rc = mParent->findRemoveableMedium(dev.deviceType,
8840 dev.uuid,
8841 false /* fRefresh */,
8842 false /* aSetError */,
8843 medium);
8844 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8845 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8846 rc = S_OK;
8847 break;
8848
8849 case DeviceType_HardDisk:
8850 {
8851 /* find a hard disk by UUID */
8852 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8853 if (FAILED(rc))
8854 {
8855 if (isSnapshotMachine())
8856 {
8857 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8858 // so the user knows that the bad disk is in a snapshot somewhere
8859 com::ErrorInfo info;
8860 return setError(E_FAIL,
8861 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8862 puuidSnapshot->raw(),
8863 info.getText().raw());
8864 }
8865 else
8866 return rc;
8867 }
8868
8869 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8870
8871 if (medium->getType() == MediumType_Immutable)
8872 {
8873 if (isSnapshotMachine())
8874 return setError(E_FAIL,
8875 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8876 "of the virtual machine '%s' ('%s')"),
8877 medium->getLocationFull().c_str(),
8878 dev.uuid.raw(),
8879 puuidSnapshot->raw(),
8880 mUserData->s.strName.c_str(),
8881 mData->m_strConfigFileFull.c_str());
8882
8883 return setError(E_FAIL,
8884 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8885 medium->getLocationFull().c_str(),
8886 dev.uuid.raw(),
8887 mUserData->s.strName.c_str(),
8888 mData->m_strConfigFileFull.c_str());
8889 }
8890
8891 if (medium->getType() == MediumType_MultiAttach)
8892 {
8893 if (isSnapshotMachine())
8894 return setError(E_FAIL,
8895 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8896 "of the virtual machine '%s' ('%s')"),
8897 medium->getLocationFull().c_str(),
8898 dev.uuid.raw(),
8899 puuidSnapshot->raw(),
8900 mUserData->s.strName.c_str(),
8901 mData->m_strConfigFileFull.c_str());
8902
8903 return setError(E_FAIL,
8904 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8905 medium->getLocationFull().c_str(),
8906 dev.uuid.raw(),
8907 mUserData->s.strName.c_str(),
8908 mData->m_strConfigFileFull.c_str());
8909 }
8910
8911 if ( !isSnapshotMachine()
8912 && medium->getChildren().size() != 0
8913 )
8914 return setError(E_FAIL,
8915 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8916 "because it has %d differencing child hard disks"),
8917 medium->getLocationFull().c_str(),
8918 dev.uuid.raw(),
8919 mUserData->s.strName.c_str(),
8920 mData->m_strConfigFileFull.c_str(),
8921 medium->getChildren().size());
8922
8923 if (findAttachment(mMediaData->mAttachments,
8924 medium))
8925 return setError(E_FAIL,
8926 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8927 medium->getLocationFull().c_str(),
8928 dev.uuid.raw(),
8929 mUserData->s.strName.c_str(),
8930 mData->m_strConfigFileFull.c_str());
8931
8932 break;
8933 }
8934
8935 default:
8936 return setError(E_FAIL,
8937 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8938 medium->getLocationFull().c_str(),
8939 mUserData->s.strName.c_str(),
8940 mData->m_strConfigFileFull.c_str());
8941 }
8942
8943 if (FAILED(rc))
8944 break;
8945
8946 /* Bandwidth groups are loaded at this point. */
8947 ComObjPtr<BandwidthGroup> pBwGroup;
8948
8949 if (!dev.strBwGroup.isEmpty())
8950 {
8951 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8952 if (FAILED(rc))
8953 return setError(E_FAIL,
8954 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8955 medium->getLocationFull().c_str(),
8956 dev.strBwGroup.c_str(),
8957 mUserData->s.strName.c_str(),
8958 mData->m_strConfigFileFull.c_str());
8959 pBwGroup->reference();
8960 }
8961
8962 const Bstr controllerName = aStorageController->getName();
8963 ComObjPtr<MediumAttachment> pAttachment;
8964 pAttachment.createObject();
8965 rc = pAttachment->init(this,
8966 medium,
8967 controllerName,
8968 dev.lPort,
8969 dev.lDevice,
8970 dev.deviceType,
8971 false,
8972 dev.fPassThrough,
8973 dev.fTempEject,
8974 dev.fNonRotational,
8975 dev.fDiscard,
8976 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8977 if (FAILED(rc)) break;
8978
8979 /* associate the medium with this machine and snapshot */
8980 if (!medium.isNull())
8981 {
8982 AutoCaller medCaller(medium);
8983 if (FAILED(medCaller.rc())) return medCaller.rc();
8984 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8985
8986 if (isSnapshotMachine())
8987 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8988 else
8989 rc = medium->addBackReference(mData->mUuid);
8990 /* If the medium->addBackReference fails it sets an appropriate
8991 * error message, so no need to do any guesswork here. */
8992
8993 if (puuidRegistry)
8994 // caller wants registry ID to be set on all attached media (OVF import case)
8995 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8996 }
8997
8998 if (FAILED(rc))
8999 break;
9000
9001 /* back up mMediaData to let registeredInit() properly rollback on failure
9002 * (= limited accessibility) */
9003 setModified(IsModified_Storage);
9004 mMediaData.backup();
9005 mMediaData->mAttachments.push_back(pAttachment);
9006 }
9007
9008 return rc;
9009}
9010
9011/**
9012 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9013 *
9014 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9015 * @param aSnapshot where to return the found snapshot
9016 * @param aSetError true to set extended error info on failure
9017 */
9018HRESULT Machine::findSnapshotById(const Guid &aId,
9019 ComObjPtr<Snapshot> &aSnapshot,
9020 bool aSetError /* = false */)
9021{
9022 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9023
9024 if (!mData->mFirstSnapshot)
9025 {
9026 if (aSetError)
9027 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9028 return E_FAIL;
9029 }
9030
9031 if (aId.isEmpty())
9032 aSnapshot = mData->mFirstSnapshot;
9033 else
9034 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9035
9036 if (!aSnapshot)
9037 {
9038 if (aSetError)
9039 return setError(E_FAIL,
9040 tr("Could not find a snapshot with UUID {%s}"),
9041 aId.toString().c_str());
9042 return E_FAIL;
9043 }
9044
9045 return S_OK;
9046}
9047
9048/**
9049 * Returns the snapshot with the given name or fails of no such snapshot.
9050 *
9051 * @param aName snapshot name to find
9052 * @param aSnapshot where to return the found snapshot
9053 * @param aSetError true to set extended error info on failure
9054 */
9055HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9056 ComObjPtr<Snapshot> &aSnapshot,
9057 bool aSetError /* = false */)
9058{
9059 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9060
9061 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9062
9063 if (!mData->mFirstSnapshot)
9064 {
9065 if (aSetError)
9066 return setError(VBOX_E_OBJECT_NOT_FOUND,
9067 tr("This machine does not have any snapshots"));
9068 return VBOX_E_OBJECT_NOT_FOUND;
9069 }
9070
9071 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9072
9073 if (!aSnapshot)
9074 {
9075 if (aSetError)
9076 return setError(VBOX_E_OBJECT_NOT_FOUND,
9077 tr("Could not find a snapshot named '%s'"), strName.c_str());
9078 return VBOX_E_OBJECT_NOT_FOUND;
9079 }
9080
9081 return S_OK;
9082}
9083
9084/**
9085 * Returns a storage controller object with the given name.
9086 *
9087 * @param aName storage controller name to find
9088 * @param aStorageController where to return the found storage controller
9089 * @param aSetError true to set extended error info on failure
9090 */
9091HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9092 ComObjPtr<StorageController> &aStorageController,
9093 bool aSetError /* = false */)
9094{
9095 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9096
9097 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9098 it != mStorageControllers->end();
9099 ++it)
9100 {
9101 if ((*it)->getName() == aName)
9102 {
9103 aStorageController = (*it);
9104 return S_OK;
9105 }
9106 }
9107
9108 if (aSetError)
9109 return setError(VBOX_E_OBJECT_NOT_FOUND,
9110 tr("Could not find a storage controller named '%s'"),
9111 aName.c_str());
9112 return VBOX_E_OBJECT_NOT_FOUND;
9113}
9114
9115HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9116 MediaData::AttachmentList &atts)
9117{
9118 AutoCaller autoCaller(this);
9119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9120
9121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9122
9123 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9124 it != mMediaData->mAttachments.end();
9125 ++it)
9126 {
9127 const ComObjPtr<MediumAttachment> &pAtt = *it;
9128
9129 // should never happen, but deal with NULL pointers in the list.
9130 AssertStmt(!pAtt.isNull(), continue);
9131
9132 // getControllerName() needs caller+read lock
9133 AutoCaller autoAttCaller(pAtt);
9134 if (FAILED(autoAttCaller.rc()))
9135 {
9136 atts.clear();
9137 return autoAttCaller.rc();
9138 }
9139 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9140
9141 if (pAtt->getControllerName() == aName)
9142 atts.push_back(pAtt);
9143 }
9144
9145 return S_OK;
9146}
9147
9148/**
9149 * Helper for #saveSettings. Cares about renaming the settings directory and
9150 * file if the machine name was changed and about creating a new settings file
9151 * if this is a new machine.
9152 *
9153 * @note Must be never called directly but only from #saveSettings().
9154 */
9155HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9156{
9157 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9158
9159 HRESULT rc = S_OK;
9160
9161 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9162
9163 /// @todo need to handle primary group change, too
9164
9165 /* attempt to rename the settings file if machine name is changed */
9166 if ( mUserData->s.fNameSync
9167 && mUserData.isBackedUp()
9168 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9169 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9170 )
9171 {
9172 bool dirRenamed = false;
9173 bool fileRenamed = false;
9174
9175 Utf8Str configFile, newConfigFile;
9176 Utf8Str configFilePrev, newConfigFilePrev;
9177 Utf8Str configDir, newConfigDir;
9178
9179 do
9180 {
9181 int vrc = VINF_SUCCESS;
9182
9183 Utf8Str name = mUserData.backedUpData()->s.strName;
9184 Utf8Str newName = mUserData->s.strName;
9185 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9186 if (group == "/")
9187 group.setNull();
9188 Utf8Str newGroup = mUserData->s.llGroups.front();
9189 if (newGroup == "/")
9190 newGroup.setNull();
9191
9192 configFile = mData->m_strConfigFileFull;
9193
9194 /* first, rename the directory if it matches the group and machine name */
9195 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9196 group.c_str(), RTPATH_DELIMITER, name.c_str());
9197 /** @todo hack, make somehow use of ComposeMachineFilename */
9198 if (mUserData->s.fDirectoryIncludesUUID)
9199 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9200 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9201 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9202 /** @todo hack, make somehow use of ComposeMachineFilename */
9203 if (mUserData->s.fDirectoryIncludesUUID)
9204 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9205 configDir = configFile;
9206 configDir.stripFilename();
9207 newConfigDir = configDir;
9208 if ( configDir.length() >= groupPlusName.length()
9209 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9210 {
9211 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9212 Utf8Str newConfigBaseDir(newConfigDir);
9213 newConfigDir.append(newGroupPlusName);
9214 /* consistency: use \ if appropriate on the platform */
9215 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9216 /* new dir and old dir cannot be equal here because of 'if'
9217 * above and because name != newName */
9218 Assert(configDir != newConfigDir);
9219 if (!fSettingsFileIsNew)
9220 {
9221 /* perform real rename only if the machine is not new */
9222 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9223 if ( vrc == VERR_FILE_NOT_FOUND
9224 || vrc == VERR_PATH_NOT_FOUND)
9225 {
9226 /* create the parent directory, then retry renaming */
9227 Utf8Str parent(newConfigDir);
9228 parent.stripFilename();
9229 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9230 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9231 }
9232 if (RT_FAILURE(vrc))
9233 {
9234 rc = setError(E_FAIL,
9235 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9236 configDir.c_str(),
9237 newConfigDir.c_str(),
9238 vrc);
9239 break;
9240 }
9241 /* delete subdirectories which are no longer needed */
9242 Utf8Str dir(configDir);
9243 dir.stripFilename();
9244 while (dir != newConfigBaseDir && dir != ".")
9245 {
9246 vrc = RTDirRemove(dir.c_str());
9247 if (RT_FAILURE(vrc))
9248 break;
9249 dir.stripFilename();
9250 }
9251 dirRenamed = true;
9252 }
9253 }
9254
9255 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9256 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9257
9258 /* then try to rename the settings file itself */
9259 if (newConfigFile != configFile)
9260 {
9261 /* get the path to old settings file in renamed directory */
9262 configFile = Utf8StrFmt("%s%c%s",
9263 newConfigDir.c_str(),
9264 RTPATH_DELIMITER,
9265 RTPathFilename(configFile.c_str()));
9266 if (!fSettingsFileIsNew)
9267 {
9268 /* perform real rename only if the machine is not new */
9269 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9270 if (RT_FAILURE(vrc))
9271 {
9272 rc = setError(E_FAIL,
9273 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9274 configFile.c_str(),
9275 newConfigFile.c_str(),
9276 vrc);
9277 break;
9278 }
9279 fileRenamed = true;
9280 configFilePrev = configFile;
9281 configFilePrev += "-prev";
9282 newConfigFilePrev = newConfigFile;
9283 newConfigFilePrev += "-prev";
9284 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9285 }
9286 }
9287
9288 // update m_strConfigFileFull amd mConfigFile
9289 mData->m_strConfigFileFull = newConfigFile;
9290 // compute the relative path too
9291 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9292
9293 // store the old and new so that VirtualBox::saveSettings() can update
9294 // the media registry
9295 if ( mData->mRegistered
9296 && configDir != newConfigDir)
9297 {
9298 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9299
9300 if (pfNeedsGlobalSaveSettings)
9301 *pfNeedsGlobalSaveSettings = true;
9302 }
9303
9304 // in the saved state file path, replace the old directory with the new directory
9305 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9306 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9307
9308 // and do the same thing for the saved state file paths of all the online snapshots
9309 if (mData->mFirstSnapshot)
9310 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9311 newConfigDir.c_str());
9312 }
9313 while (0);
9314
9315 if (FAILED(rc))
9316 {
9317 /* silently try to rename everything back */
9318 if (fileRenamed)
9319 {
9320 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9321 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9322 }
9323 if (dirRenamed)
9324 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9325 }
9326
9327 if (FAILED(rc)) return rc;
9328 }
9329
9330 if (fSettingsFileIsNew)
9331 {
9332 /* create a virgin config file */
9333 int vrc = VINF_SUCCESS;
9334
9335 /* ensure the settings directory exists */
9336 Utf8Str path(mData->m_strConfigFileFull);
9337 path.stripFilename();
9338 if (!RTDirExists(path.c_str()))
9339 {
9340 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9341 if (RT_FAILURE(vrc))
9342 {
9343 return setError(E_FAIL,
9344 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9345 path.c_str(),
9346 vrc);
9347 }
9348 }
9349
9350 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9351 path = Utf8Str(mData->m_strConfigFileFull);
9352 RTFILE f = NIL_RTFILE;
9353 vrc = RTFileOpen(&f, path.c_str(),
9354 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9355 if (RT_FAILURE(vrc))
9356 return setError(E_FAIL,
9357 tr("Could not create the settings file '%s' (%Rrc)"),
9358 path.c_str(),
9359 vrc);
9360 RTFileClose(f);
9361 }
9362
9363 return rc;
9364}
9365
9366/**
9367 * Saves and commits machine data, user data and hardware data.
9368 *
9369 * Note that on failure, the data remains uncommitted.
9370 *
9371 * @a aFlags may combine the following flags:
9372 *
9373 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9374 * Used when saving settings after an operation that makes them 100%
9375 * correspond to the settings from the current snapshot.
9376 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9377 * #isReallyModified() returns false. This is necessary for cases when we
9378 * change machine data directly, not through the backup()/commit() mechanism.
9379 * - SaveS_Force: settings will be saved without doing a deep compare of the
9380 * settings structures. This is used when this is called because snapshots
9381 * have changed to avoid the overhead of the deep compare.
9382 *
9383 * @note Must be called from under this object's write lock. Locks children for
9384 * writing.
9385 *
9386 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9387 * initialized to false and that will be set to true by this function if
9388 * the caller must invoke VirtualBox::saveSettings() because the global
9389 * settings have changed. This will happen if a machine rename has been
9390 * saved and the global machine and media registries will therefore need
9391 * updating.
9392 */
9393HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9394 int aFlags /*= 0*/)
9395{
9396 LogFlowThisFuncEnter();
9397
9398 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9399
9400 /* make sure child objects are unable to modify the settings while we are
9401 * saving them */
9402 ensureNoStateDependencies();
9403
9404 AssertReturn(!isSnapshotMachine(),
9405 E_FAIL);
9406
9407 HRESULT rc = S_OK;
9408 bool fNeedsWrite = false;
9409
9410 /* First, prepare to save settings. It will care about renaming the
9411 * settings directory and file if the machine name was changed and about
9412 * creating a new settings file if this is a new machine. */
9413 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9414 if (FAILED(rc)) return rc;
9415
9416 // keep a pointer to the current settings structures
9417 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9418 settings::MachineConfigFile *pNewConfig = NULL;
9419
9420 try
9421 {
9422 // make a fresh one to have everyone write stuff into
9423 pNewConfig = new settings::MachineConfigFile(NULL);
9424 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9425
9426 // now go and copy all the settings data from COM to the settings structures
9427 // (this calles saveSettings() on all the COM objects in the machine)
9428 copyMachineDataToSettings(*pNewConfig);
9429
9430 if (aFlags & SaveS_ResetCurStateModified)
9431 {
9432 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9433 mData->mCurrentStateModified = FALSE;
9434 fNeedsWrite = true; // always, no need to compare
9435 }
9436 else if (aFlags & SaveS_Force)
9437 {
9438 fNeedsWrite = true; // always, no need to compare
9439 }
9440 else
9441 {
9442 if (!mData->mCurrentStateModified)
9443 {
9444 // do a deep compare of the settings that we just saved with the settings
9445 // previously stored in the config file; this invokes MachineConfigFile::operator==
9446 // which does a deep compare of all the settings, which is expensive but less expensive
9447 // than writing out XML in vain
9448 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9449
9450 // could still be modified if any settings changed
9451 mData->mCurrentStateModified = fAnySettingsChanged;
9452
9453 fNeedsWrite = fAnySettingsChanged;
9454 }
9455 else
9456 fNeedsWrite = true;
9457 }
9458
9459 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9460
9461 if (fNeedsWrite)
9462 // now spit it all out!
9463 pNewConfig->write(mData->m_strConfigFileFull);
9464
9465 mData->pMachineConfigFile = pNewConfig;
9466 delete pOldConfig;
9467 commit();
9468
9469 // after saving settings, we are no longer different from the XML on disk
9470 mData->flModifications = 0;
9471 }
9472 catch (HRESULT err)
9473 {
9474 // we assume that error info is set by the thrower
9475 rc = err;
9476
9477 // restore old config
9478 delete pNewConfig;
9479 mData->pMachineConfigFile = pOldConfig;
9480 }
9481 catch (...)
9482 {
9483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9484 }
9485
9486 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9487 {
9488 /* Fire the data change event, even on failure (since we've already
9489 * committed all data). This is done only for SessionMachines because
9490 * mutable Machine instances are always not registered (i.e. private
9491 * to the client process that creates them) and thus don't need to
9492 * inform callbacks. */
9493 if (isSessionMachine())
9494 mParent->onMachineDataChange(mData->mUuid);
9495 }
9496
9497 LogFlowThisFunc(("rc=%08X\n", rc));
9498 LogFlowThisFuncLeave();
9499 return rc;
9500}
9501
9502/**
9503 * Implementation for saving the machine settings into the given
9504 * settings::MachineConfigFile instance. This copies machine extradata
9505 * from the previous machine config file in the instance data, if any.
9506 *
9507 * This gets called from two locations:
9508 *
9509 * -- Machine::saveSettings(), during the regular XML writing;
9510 *
9511 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9512 * exported to OVF and we write the VirtualBox proprietary XML
9513 * into a <vbox:Machine> tag.
9514 *
9515 * This routine fills all the fields in there, including snapshots, *except*
9516 * for the following:
9517 *
9518 * -- fCurrentStateModified. There is some special logic associated with that.
9519 *
9520 * The caller can then call MachineConfigFile::write() or do something else
9521 * with it.
9522 *
9523 * Caller must hold the machine lock!
9524 *
9525 * This throws XML errors and HRESULT, so the caller must have a catch block!
9526 */
9527void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9528{
9529 // deep copy extradata
9530 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9531
9532 config.uuid = mData->mUuid;
9533
9534 // copy name, description, OS type, teleport, UTC etc.
9535 config.machineUserData = mUserData->s;
9536
9537 if ( mData->mMachineState == MachineState_Saved
9538 || mData->mMachineState == MachineState_Restoring
9539 // when deleting a snapshot we may or may not have a saved state in the current state,
9540 // so let's not assert here please
9541 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9542 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9543 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9544 && (!mSSData->strStateFilePath.isEmpty())
9545 )
9546 )
9547 {
9548 Assert(!mSSData->strStateFilePath.isEmpty());
9549 /* try to make the file name relative to the settings file dir */
9550 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9551 }
9552 else
9553 {
9554 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9555 config.strStateFile.setNull();
9556 }
9557
9558 if (mData->mCurrentSnapshot)
9559 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9560 else
9561 config.uuidCurrentSnapshot.clear();
9562
9563 config.timeLastStateChange = mData->mLastStateChange;
9564 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9565 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9566
9567 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9568 if (FAILED(rc)) throw rc;
9569
9570 rc = saveStorageControllers(config.storageMachine);
9571 if (FAILED(rc)) throw rc;
9572
9573 // save machine's media registry if this is VirtualBox 4.0 or later
9574 if (config.canHaveOwnMediaRegistry())
9575 {
9576 // determine machine folder
9577 Utf8Str strMachineFolder = getSettingsFileFull();
9578 strMachineFolder.stripFilename();
9579 mParent->saveMediaRegistry(config.mediaRegistry,
9580 getId(), // only media with registry ID == machine UUID
9581 strMachineFolder);
9582 // this throws HRESULT
9583 }
9584
9585 // save snapshots
9586 rc = saveAllSnapshots(config);
9587 if (FAILED(rc)) throw rc;
9588}
9589
9590/**
9591 * Saves all snapshots of the machine into the given machine config file. Called
9592 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9593 * @param config
9594 * @return
9595 */
9596HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9597{
9598 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9599
9600 HRESULT rc = S_OK;
9601
9602 try
9603 {
9604 config.llFirstSnapshot.clear();
9605
9606 if (mData->mFirstSnapshot)
9607 {
9608 settings::Snapshot snapNew;
9609 config.llFirstSnapshot.push_back(snapNew);
9610
9611 // get reference to the fresh copy of the snapshot on the list and
9612 // work on that copy directly to avoid excessive copying later
9613 settings::Snapshot &snap = config.llFirstSnapshot.front();
9614
9615 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9616 if (FAILED(rc)) throw rc;
9617 }
9618
9619// if (mType == IsSessionMachine)
9620// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9621
9622 }
9623 catch (HRESULT err)
9624 {
9625 /* we assume that error info is set by the thrower */
9626 rc = err;
9627 }
9628 catch (...)
9629 {
9630 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9631 }
9632
9633 return rc;
9634}
9635
9636/**
9637 * Saves the VM hardware configuration. It is assumed that the
9638 * given node is empty.
9639 *
9640 * @param data Reference to the settings object for the hardware config.
9641 * @param pDbg Pointer to the settings object for the debugging config
9642 * which happens to live in mHWData.
9643 * @param pAutostart Pointer to the settings object for the autostart config
9644 * which happens to live in mHWData.
9645 */
9646HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9647 settings::Autostart *pAutostart)
9648{
9649 HRESULT rc = S_OK;
9650
9651 try
9652 {
9653 /* The hardware version attribute (optional).
9654 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9655 if ( mHWData->mHWVersion == "1"
9656 && mSSData->strStateFilePath.isEmpty()
9657 )
9658 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
9659
9660 data.strVersion = mHWData->mHWVersion;
9661 data.uuid = mHWData->mHardwareUUID;
9662
9663 // CPU
9664 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9665 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9666 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9667 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9668 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9669 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9670 data.fPAE = !!mHWData->mPAEEnabled;
9671 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9672
9673 /* Standard and Extended CPUID leafs. */
9674 data.llCpuIdLeafs.clear();
9675 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9676 {
9677 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9678 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9679 }
9680 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9681 {
9682 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9683 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9684 }
9685
9686 data.cCPUs = mHWData->mCPUCount;
9687 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9688 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9689
9690 data.llCpus.clear();
9691 if (data.fCpuHotPlug)
9692 {
9693 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9694 {
9695 if (mHWData->mCPUAttached[idx])
9696 {
9697 settings::Cpu cpu;
9698 cpu.ulId = idx;
9699 data.llCpus.push_back(cpu);
9700 }
9701 }
9702 }
9703
9704 // memory
9705 data.ulMemorySizeMB = mHWData->mMemorySize;
9706 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9707
9708 // firmware
9709 data.firmwareType = mHWData->mFirmwareType;
9710
9711 // HID
9712 data.pointingHIDType = mHWData->mPointingHIDType;
9713 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9714
9715 // chipset
9716 data.chipsetType = mHWData->mChipsetType;
9717
9718 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9719
9720 // HPET
9721 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9722
9723 // boot order
9724 data.mapBootOrder.clear();
9725 for (size_t i = 0;
9726 i < RT_ELEMENTS(mHWData->mBootOrder);
9727 ++i)
9728 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9729
9730 // display
9731 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9732 data.cMonitors = mHWData->mMonitorCount;
9733 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9734 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9735 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9736 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9737 data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled;
9738 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9739
9740 /* VRDEServer settings (optional) */
9741 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9742 if (FAILED(rc)) throw rc;
9743
9744 /* BIOS (required) */
9745 rc = mBIOSSettings->saveSettings(data.biosSettings);
9746 if (FAILED(rc)) throw rc;
9747
9748 /* USB Controller (required) */
9749 rc = mUSBController->saveSettings(data.usbController);
9750 if (FAILED(rc)) throw rc;
9751
9752 /* Network adapters (required) */
9753 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9754 data.llNetworkAdapters.clear();
9755 /* Write out only the nominal number of network adapters for this
9756 * chipset type. Since Machine::commit() hasn't been called there
9757 * may be extra NIC settings in the vector. */
9758 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9759 {
9760 settings::NetworkAdapter nic;
9761 nic.ulSlot = slot;
9762 /* paranoia check... must not be NULL, but must not crash either. */
9763 if (mNetworkAdapters[slot])
9764 {
9765 rc = mNetworkAdapters[slot]->saveSettings(nic);
9766 if (FAILED(rc)) throw rc;
9767
9768 data.llNetworkAdapters.push_back(nic);
9769 }
9770 }
9771
9772 /* Serial ports */
9773 data.llSerialPorts.clear();
9774 for (ULONG slot = 0;
9775 slot < RT_ELEMENTS(mSerialPorts);
9776 ++slot)
9777 {
9778 settings::SerialPort s;
9779 s.ulSlot = slot;
9780 rc = mSerialPorts[slot]->saveSettings(s);
9781 if (FAILED(rc)) return rc;
9782
9783 data.llSerialPorts.push_back(s);
9784 }
9785
9786 /* Parallel ports */
9787 data.llParallelPorts.clear();
9788 for (ULONG slot = 0;
9789 slot < RT_ELEMENTS(mParallelPorts);
9790 ++slot)
9791 {
9792 settings::ParallelPort p;
9793 p.ulSlot = slot;
9794 rc = mParallelPorts[slot]->saveSettings(p);
9795 if (FAILED(rc)) return rc;
9796
9797 data.llParallelPorts.push_back(p);
9798 }
9799
9800 /* Audio adapter */
9801 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9802 if (FAILED(rc)) return rc;
9803
9804 /* Shared folders */
9805 data.llSharedFolders.clear();
9806 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9807 it != mHWData->mSharedFolders.end();
9808 ++it)
9809 {
9810 SharedFolder *pSF = *it;
9811 AutoCaller sfCaller(pSF);
9812 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9813 settings::SharedFolder sf;
9814 sf.strName = pSF->getName();
9815 sf.strHostPath = pSF->getHostPath();
9816 sf.fWritable = !!pSF->isWritable();
9817 sf.fAutoMount = !!pSF->isAutoMounted();
9818
9819 data.llSharedFolders.push_back(sf);
9820 }
9821
9822 // clipboard
9823 data.clipboardMode = mHWData->mClipboardMode;
9824
9825 // drag'n'drop
9826 data.dragAndDropMode = mHWData->mDragAndDropMode;
9827
9828 /* Guest */
9829 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9830
9831 // IO settings
9832 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9833 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9834
9835 /* BandwidthControl (required) */
9836 rc = mBandwidthControl->saveSettings(data.ioSettings);
9837 if (FAILED(rc)) throw rc;
9838
9839 /* Host PCI devices */
9840 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9841 it != mHWData->mPCIDeviceAssignments.end();
9842 ++it)
9843 {
9844 ComObjPtr<PCIDeviceAttachment> pda = *it;
9845 settings::HostPCIDeviceAttachment hpda;
9846
9847 rc = pda->saveSettings(hpda);
9848 if (FAILED(rc)) throw rc;
9849
9850 data.pciAttachments.push_back(hpda);
9851 }
9852
9853
9854 // guest properties
9855 data.llGuestProperties.clear();
9856#ifdef VBOX_WITH_GUEST_PROPS
9857 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9858 it != mHWData->mGuestProperties.end();
9859 ++it)
9860 {
9861 HWData::GuestProperty property = *it;
9862
9863 /* Remove transient guest properties at shutdown unless we
9864 * are saving state */
9865 if ( ( mData->mMachineState == MachineState_PoweredOff
9866 || mData->mMachineState == MachineState_Aborted
9867 || mData->mMachineState == MachineState_Teleported)
9868 && ( property.mFlags & guestProp::TRANSIENT
9869 || property.mFlags & guestProp::TRANSRESET))
9870 continue;
9871 settings::GuestProperty prop;
9872 prop.strName = property.strName;
9873 prop.strValue = property.strValue;
9874 prop.timestamp = property.mTimestamp;
9875 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9876 guestProp::writeFlags(property.mFlags, szFlags);
9877 prop.strFlags = szFlags;
9878
9879 data.llGuestProperties.push_back(prop);
9880 }
9881
9882 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9883 /* I presume this doesn't require a backup(). */
9884 mData->mGuestPropertiesModified = FALSE;
9885#endif /* VBOX_WITH_GUEST_PROPS defined */
9886
9887 *pDbg = mHWData->mDebugging;
9888 *pAutostart = mHWData->mAutostart;
9889 }
9890 catch(std::bad_alloc &)
9891 {
9892 return E_OUTOFMEMORY;
9893 }
9894
9895 AssertComRC(rc);
9896 return rc;
9897}
9898
9899/**
9900 * Saves the storage controller configuration.
9901 *
9902 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9903 */
9904HRESULT Machine::saveStorageControllers(settings::Storage &data)
9905{
9906 data.llStorageControllers.clear();
9907
9908 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9909 it != mStorageControllers->end();
9910 ++it)
9911 {
9912 HRESULT rc;
9913 ComObjPtr<StorageController> pCtl = *it;
9914
9915 settings::StorageController ctl;
9916 ctl.strName = pCtl->getName();
9917 ctl.controllerType = pCtl->getControllerType();
9918 ctl.storageBus = pCtl->getStorageBus();
9919 ctl.ulInstance = pCtl->getInstance();
9920 ctl.fBootable = pCtl->getBootable();
9921
9922 /* Save the port count. */
9923 ULONG portCount;
9924 rc = pCtl->COMGETTER(PortCount)(&portCount);
9925 ComAssertComRCRet(rc, rc);
9926 ctl.ulPortCount = portCount;
9927
9928 /* Save fUseHostIOCache */
9929 BOOL fUseHostIOCache;
9930 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9931 ComAssertComRCRet(rc, rc);
9932 ctl.fUseHostIOCache = !!fUseHostIOCache;
9933
9934 /* Save IDE emulation settings. */
9935 if (ctl.controllerType == StorageControllerType_IntelAhci)
9936 {
9937 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9938 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9939 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9940 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9941 )
9942 ComAssertComRCRet(rc, rc);
9943 }
9944
9945 /* save the devices now. */
9946 rc = saveStorageDevices(pCtl, ctl);
9947 ComAssertComRCRet(rc, rc);
9948
9949 data.llStorageControllers.push_back(ctl);
9950 }
9951
9952 return S_OK;
9953}
9954
9955/**
9956 * Saves the hard disk configuration.
9957 */
9958HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9959 settings::StorageController &data)
9960{
9961 MediaData::AttachmentList atts;
9962
9963 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9964 if (FAILED(rc)) return rc;
9965
9966 data.llAttachedDevices.clear();
9967 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9968 it != atts.end();
9969 ++it)
9970 {
9971 settings::AttachedDevice dev;
9972
9973 MediumAttachment *pAttach = *it;
9974 Medium *pMedium = pAttach->getMedium();
9975
9976 dev.deviceType = pAttach->getType();
9977 dev.lPort = pAttach->getPort();
9978 dev.lDevice = pAttach->getDevice();
9979 if (pMedium)
9980 {
9981 if (pMedium->isHostDrive())
9982 dev.strHostDriveSrc = pMedium->getLocationFull();
9983 else
9984 dev.uuid = pMedium->getId();
9985 dev.fPassThrough = pAttach->getPassthrough();
9986 dev.fTempEject = pAttach->getTempEject();
9987 dev.fDiscard = pAttach->getDiscard();
9988 }
9989
9990 dev.strBwGroup = pAttach->getBandwidthGroup();
9991
9992 data.llAttachedDevices.push_back(dev);
9993 }
9994
9995 return S_OK;
9996}
9997
9998/**
9999 * Saves machine state settings as defined by aFlags
10000 * (SaveSTS_* values).
10001 *
10002 * @param aFlags Combination of SaveSTS_* flags.
10003 *
10004 * @note Locks objects for writing.
10005 */
10006HRESULT Machine::saveStateSettings(int aFlags)
10007{
10008 if (aFlags == 0)
10009 return S_OK;
10010
10011 AutoCaller autoCaller(this);
10012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10013
10014 /* This object's write lock is also necessary to serialize file access
10015 * (prevent concurrent reads and writes) */
10016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10017
10018 HRESULT rc = S_OK;
10019
10020 Assert(mData->pMachineConfigFile);
10021
10022 try
10023 {
10024 if (aFlags & SaveSTS_CurStateModified)
10025 mData->pMachineConfigFile->fCurrentStateModified = true;
10026
10027 if (aFlags & SaveSTS_StateFilePath)
10028 {
10029 if (!mSSData->strStateFilePath.isEmpty())
10030 /* try to make the file name relative to the settings file dir */
10031 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10032 else
10033 mData->pMachineConfigFile->strStateFile.setNull();
10034 }
10035
10036 if (aFlags & SaveSTS_StateTimeStamp)
10037 {
10038 Assert( mData->mMachineState != MachineState_Aborted
10039 || mSSData->strStateFilePath.isEmpty());
10040
10041 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10042
10043 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10044//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10045 }
10046
10047 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10048 }
10049 catch (...)
10050 {
10051 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10052 }
10053
10054 return rc;
10055}
10056
10057/**
10058 * Ensures that the given medium is added to a media registry. If this machine
10059 * was created with 4.0 or later, then the machine registry is used. Otherwise
10060 * the global VirtualBox media registry is used.
10061 *
10062 * Caller must NOT hold machine lock, media tree or any medium locks!
10063 *
10064 * @param pMedium
10065 */
10066void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10067{
10068 /* Paranoia checks: do not hold machine or media tree locks. */
10069 AssertReturnVoid(!isWriteLockOnCurrentThread());
10070 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10071
10072 ComObjPtr<Medium> pBase;
10073 {
10074 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10075 pBase = pMedium->getBase();
10076 }
10077
10078 /* Paranoia checks: do not hold medium locks. */
10079 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10080 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10081
10082 // decide which medium registry to use now that the medium is attached:
10083 Guid uuid;
10084 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10085 // machine XML is VirtualBox 4.0 or higher:
10086 uuid = getId(); // machine UUID
10087 else
10088 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10089
10090 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10091 mParent->markRegistryModified(uuid);
10092
10093 /* For more complex hard disk structures it can happen that the base
10094 * medium isn't yet associated with any medium registry. Do that now. */
10095 if (pMedium != pBase)
10096 {
10097 if (pBase->addRegistry(uuid, true /* fRecurse */))
10098 mParent->markRegistryModified(uuid);
10099 }
10100}
10101
10102/**
10103 * Creates differencing hard disks for all normal hard disks attached to this
10104 * machine and a new set of attachments to refer to created disks.
10105 *
10106 * Used when taking a snapshot or when deleting the current state. Gets called
10107 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10108 *
10109 * This method assumes that mMediaData contains the original hard disk attachments
10110 * it needs to create diffs for. On success, these attachments will be replaced
10111 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10112 * called to delete created diffs which will also rollback mMediaData and restore
10113 * whatever was backed up before calling this method.
10114 *
10115 * Attachments with non-normal hard disks are left as is.
10116 *
10117 * If @a aOnline is @c false then the original hard disks that require implicit
10118 * diffs will be locked for reading. Otherwise it is assumed that they are
10119 * already locked for writing (when the VM was started). Note that in the latter
10120 * case it is responsibility of the caller to lock the newly created diffs for
10121 * writing if this method succeeds.
10122 *
10123 * @param aProgress Progress object to run (must contain at least as
10124 * many operations left as the number of hard disks
10125 * attached).
10126 * @param aOnline Whether the VM was online prior to this operation.
10127 *
10128 * @note The progress object is not marked as completed, neither on success nor
10129 * on failure. This is a responsibility of the caller.
10130 *
10131 * @note Locks this object for writing.
10132 */
10133HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10134 ULONG aWeight,
10135 bool aOnline)
10136{
10137 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10138
10139 AutoCaller autoCaller(this);
10140 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10141
10142 AutoMultiWriteLock2 alock(this->lockHandle(),
10143 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10144
10145 /* must be in a protective state because we release the lock below */
10146 AssertReturn( mData->mMachineState == MachineState_Saving
10147 || mData->mMachineState == MachineState_LiveSnapshotting
10148 || mData->mMachineState == MachineState_RestoringSnapshot
10149 || mData->mMachineState == MachineState_DeletingSnapshot
10150 , E_FAIL);
10151
10152 HRESULT rc = S_OK;
10153
10154 MediumLockListMap lockedMediaOffline;
10155 MediumLockListMap *lockedMediaMap;
10156 if (aOnline)
10157 lockedMediaMap = &mData->mSession.mLockedMedia;
10158 else
10159 lockedMediaMap = &lockedMediaOffline;
10160
10161 try
10162 {
10163 if (!aOnline)
10164 {
10165 /* lock all attached hard disks early to detect "in use"
10166 * situations before creating actual diffs */
10167 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10168 it != mMediaData->mAttachments.end();
10169 ++it)
10170 {
10171 MediumAttachment* pAtt = *it;
10172 if (pAtt->getType() == DeviceType_HardDisk)
10173 {
10174 Medium* pMedium = pAtt->getMedium();
10175 Assert(pMedium);
10176
10177 MediumLockList *pMediumLockList(new MediumLockList());
10178 alock.release();
10179 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10180 false /* fMediumLockWrite */,
10181 NULL,
10182 *pMediumLockList);
10183 alock.acquire();
10184 if (FAILED(rc))
10185 {
10186 delete pMediumLockList;
10187 throw rc;
10188 }
10189 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10190 if (FAILED(rc))
10191 {
10192 throw setError(rc,
10193 tr("Collecting locking information for all attached media failed"));
10194 }
10195 }
10196 }
10197
10198 /* Now lock all media. If this fails, nothing is locked. */
10199 alock.release();
10200 rc = lockedMediaMap->Lock();
10201 alock.acquire();
10202 if (FAILED(rc))
10203 {
10204 throw setError(rc,
10205 tr("Locking of attached media failed"));
10206 }
10207 }
10208
10209 /* remember the current list (note that we don't use backup() since
10210 * mMediaData may be already backed up) */
10211 MediaData::AttachmentList atts = mMediaData->mAttachments;
10212
10213 /* start from scratch */
10214 mMediaData->mAttachments.clear();
10215
10216 /* go through remembered attachments and create diffs for normal hard
10217 * disks and attach them */
10218 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10219 it != atts.end();
10220 ++it)
10221 {
10222 MediumAttachment* pAtt = *it;
10223
10224 DeviceType_T devType = pAtt->getType();
10225 Medium* pMedium = pAtt->getMedium();
10226
10227 if ( devType != DeviceType_HardDisk
10228 || pMedium == NULL
10229 || pMedium->getType() != MediumType_Normal)
10230 {
10231 /* copy the attachment as is */
10232
10233 /** @todo the progress object created in Console::TakeSnaphot
10234 * only expects operations for hard disks. Later other
10235 * device types need to show up in the progress as well. */
10236 if (devType == DeviceType_HardDisk)
10237 {
10238 if (pMedium == NULL)
10239 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10240 aWeight); // weight
10241 else
10242 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10243 pMedium->getBase()->getName().c_str()).raw(),
10244 aWeight); // weight
10245 }
10246
10247 mMediaData->mAttachments.push_back(pAtt);
10248 continue;
10249 }
10250
10251 /* need a diff */
10252 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10253 pMedium->getBase()->getName().c_str()).raw(),
10254 aWeight); // weight
10255
10256 Utf8Str strFullSnapshotFolder;
10257 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10258
10259 ComObjPtr<Medium> diff;
10260 diff.createObject();
10261 // store the diff in the same registry as the parent
10262 // (this cannot fail here because we can't create implicit diffs for
10263 // unregistered images)
10264 Guid uuidRegistryParent;
10265 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10266 Assert(fInRegistry); NOREF(fInRegistry);
10267 rc = diff->init(mParent,
10268 pMedium->getPreferredDiffFormat(),
10269 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10270 uuidRegistryParent);
10271 if (FAILED(rc)) throw rc;
10272
10273 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10274 * the push_back? Looks like we're going to release medium with the
10275 * wrong kind of lock (general issue with if we fail anywhere at all)
10276 * and an orphaned VDI in the snapshots folder. */
10277
10278 /* update the appropriate lock list */
10279 MediumLockList *pMediumLockList;
10280 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10281 AssertComRCThrowRC(rc);
10282 if (aOnline)
10283 {
10284 alock.release();
10285 rc = pMediumLockList->Update(pMedium, false);
10286 alock.acquire();
10287 AssertComRCThrowRC(rc);
10288 }
10289
10290 /* release the locks before the potentially lengthy operation */
10291 alock.release();
10292 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10293 pMediumLockList,
10294 NULL /* aProgress */,
10295 true /* aWait */);
10296 alock.acquire();
10297 if (FAILED(rc)) throw rc;
10298
10299 rc = lockedMediaMap->Unlock();
10300 AssertComRCThrowRC(rc);
10301 alock.release();
10302 rc = pMediumLockList->Append(diff, true);
10303 alock.acquire();
10304 AssertComRCThrowRC(rc);
10305 alock.release();
10306 rc = lockedMediaMap->Lock();
10307 alock.acquire();
10308 AssertComRCThrowRC(rc);
10309
10310 rc = diff->addBackReference(mData->mUuid);
10311 AssertComRCThrowRC(rc);
10312
10313 /* add a new attachment */
10314 ComObjPtr<MediumAttachment> attachment;
10315 attachment.createObject();
10316 rc = attachment->init(this,
10317 diff,
10318 pAtt->getControllerName(),
10319 pAtt->getPort(),
10320 pAtt->getDevice(),
10321 DeviceType_HardDisk,
10322 true /* aImplicit */,
10323 false /* aPassthrough */,
10324 false /* aTempEject */,
10325 pAtt->getNonRotational(),
10326 pAtt->getDiscard(),
10327 pAtt->getBandwidthGroup());
10328 if (FAILED(rc)) throw rc;
10329
10330 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10331 AssertComRCThrowRC(rc);
10332 mMediaData->mAttachments.push_back(attachment);
10333 }
10334 }
10335 catch (HRESULT aRC) { rc = aRC; }
10336
10337 /* unlock all hard disks we locked */
10338 if (!aOnline)
10339 {
10340 ErrorInfoKeeper eik;
10341
10342 HRESULT rc1 = lockedMediaMap->Clear();
10343 AssertComRC(rc1);
10344 }
10345
10346 if (FAILED(rc))
10347 {
10348 MultiResult mrc = rc;
10349
10350 alock.release();
10351 mrc = deleteImplicitDiffs();
10352 }
10353
10354 return rc;
10355}
10356
10357/**
10358 * Deletes implicit differencing hard disks created either by
10359 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10360 *
10361 * Note that to delete hard disks created by #AttachDevice() this method is
10362 * called from #fixupMedia() when the changes are rolled back.
10363 *
10364 * @note Locks this object for writing.
10365 */
10366HRESULT Machine::deleteImplicitDiffs()
10367{
10368 AutoCaller autoCaller(this);
10369 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10370
10371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10372 LogFlowThisFuncEnter();
10373
10374 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10375
10376 HRESULT rc = S_OK;
10377
10378 MediaData::AttachmentList implicitAtts;
10379
10380 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10381
10382 /* enumerate new attachments */
10383 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10384 it != mMediaData->mAttachments.end();
10385 ++it)
10386 {
10387 ComObjPtr<Medium> hd = (*it)->getMedium();
10388 if (hd.isNull())
10389 continue;
10390
10391 if ((*it)->isImplicit())
10392 {
10393 /* deassociate and mark for deletion */
10394 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));
10395 rc = hd->removeBackReference(mData->mUuid);
10396 AssertComRC(rc);
10397 implicitAtts.push_back(*it);
10398 continue;
10399 }
10400
10401 /* was this hard disk attached before? */
10402 if (!findAttachment(oldAtts, hd))
10403 {
10404 /* no: de-associate */
10405 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));
10406 rc = hd->removeBackReference(mData->mUuid);
10407 AssertComRC(rc);
10408 continue;
10409 }
10410 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));
10411 }
10412
10413 /* rollback hard disk changes */
10414 mMediaData.rollback();
10415
10416 MultiResult mrc(S_OK);
10417
10418 /* delete unused implicit diffs */
10419 if (implicitAtts.size() != 0)
10420 {
10421 /* will release the lock before the potentially lengthy
10422 * operation, so protect with the special state (unless already
10423 * protected) */
10424 MachineState_T oldState = mData->mMachineState;
10425 if ( oldState != MachineState_Saving
10426 && oldState != MachineState_LiveSnapshotting
10427 && oldState != MachineState_RestoringSnapshot
10428 && oldState != MachineState_DeletingSnapshot
10429 && oldState != MachineState_DeletingSnapshotOnline
10430 && oldState != MachineState_DeletingSnapshotPaused
10431 )
10432 setMachineState(MachineState_SettingUp);
10433
10434 alock.release();
10435
10436 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10437 it != implicitAtts.end();
10438 ++it)
10439 {
10440 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));
10441 ComObjPtr<Medium> hd = (*it)->getMedium();
10442
10443 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10444 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));
10445 mrc = rc;
10446 }
10447
10448 alock.acquire();
10449
10450 if (mData->mMachineState == MachineState_SettingUp)
10451 setMachineState(oldState);
10452 }
10453
10454 return mrc;
10455}
10456
10457/**
10458 * Looks through the given list of media attachments for one with the given parameters
10459 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10460 * can be searched as well if needed.
10461 *
10462 * @param list
10463 * @param aControllerName
10464 * @param aControllerPort
10465 * @param aDevice
10466 * @return
10467 */
10468MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10469 IN_BSTR aControllerName,
10470 LONG aControllerPort,
10471 LONG aDevice)
10472{
10473 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10474 it != ll.end();
10475 ++it)
10476 {
10477 MediumAttachment *pAttach = *it;
10478 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10479 return pAttach;
10480 }
10481
10482 return NULL;
10483}
10484
10485/**
10486 * Looks through the given list of media attachments for one with the given parameters
10487 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10488 * can be searched as well if needed.
10489 *
10490 * @param list
10491 * @param aControllerName
10492 * @param aControllerPort
10493 * @param aDevice
10494 * @return
10495 */
10496MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10497 ComObjPtr<Medium> pMedium)
10498{
10499 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10500 it != ll.end();
10501 ++it)
10502 {
10503 MediumAttachment *pAttach = *it;
10504 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10505 if (pMediumThis == pMedium)
10506 return pAttach;
10507 }
10508
10509 return NULL;
10510}
10511
10512/**
10513 * Looks through the given list of media attachments for one with the given parameters
10514 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10515 * can be searched as well if needed.
10516 *
10517 * @param list
10518 * @param aControllerName
10519 * @param aControllerPort
10520 * @param aDevice
10521 * @return
10522 */
10523MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10524 Guid &id)
10525{
10526 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10527 it != ll.end();
10528 ++it)
10529 {
10530 MediumAttachment *pAttach = *it;
10531 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10532 if (pMediumThis->getId() == id)
10533 return pAttach;
10534 }
10535
10536 return NULL;
10537}
10538
10539/**
10540 * Main implementation for Machine::DetachDevice. This also gets called
10541 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10542 *
10543 * @param pAttach Medium attachment to detach.
10544 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10545 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10546 * @return
10547 */
10548HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10549 AutoWriteLock &writeLock,
10550 Snapshot *pSnapshot)
10551{
10552 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10553 DeviceType_T mediumType = pAttach->getType();
10554
10555 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10556
10557 if (pAttach->isImplicit())
10558 {
10559 /* attempt to implicitly delete the implicitly created diff */
10560
10561 /// @todo move the implicit flag from MediumAttachment to Medium
10562 /// and forbid any hard disk operation when it is implicit. Or maybe
10563 /// a special media state for it to make it even more simple.
10564
10565 Assert(mMediaData.isBackedUp());
10566
10567 /* will release the lock before the potentially lengthy operation, so
10568 * protect with the special state */
10569 MachineState_T oldState = mData->mMachineState;
10570 setMachineState(MachineState_SettingUp);
10571
10572 writeLock.release();
10573
10574 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10575 true /*aWait*/);
10576
10577 writeLock.acquire();
10578
10579 setMachineState(oldState);
10580
10581 if (FAILED(rc)) return rc;
10582 }
10583
10584 setModified(IsModified_Storage);
10585 mMediaData.backup();
10586 mMediaData->mAttachments.remove(pAttach);
10587
10588 if (!oldmedium.isNull())
10589 {
10590 // if this is from a snapshot, do not defer detachment to commitMedia()
10591 if (pSnapshot)
10592 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10593 // else if non-hard disk media, do not defer detachment to commitMedia() either
10594 else if (mediumType != DeviceType_HardDisk)
10595 oldmedium->removeBackReference(mData->mUuid);
10596 }
10597
10598 return S_OK;
10599}
10600
10601/**
10602 * Goes thru all media of the given list and
10603 *
10604 * 1) calls detachDevice() on each of them for this machine and
10605 * 2) adds all Medium objects found in the process to the given list,
10606 * depending on cleanupMode.
10607 *
10608 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10609 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10610 * media to the list.
10611 *
10612 * This gets called from Machine::Unregister, both for the actual Machine and
10613 * the SnapshotMachine objects that might be found in the snapshots.
10614 *
10615 * Requires caller and locking. The machine lock must be passed in because it
10616 * will be passed on to detachDevice which needs it for temporary unlocking.
10617 *
10618 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10619 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10620 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10621 * otherwise no media get added.
10622 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10623 * @return
10624 */
10625HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10626 Snapshot *pSnapshot,
10627 CleanupMode_T cleanupMode,
10628 MediaList &llMedia)
10629{
10630 Assert(isWriteLockOnCurrentThread());
10631
10632 HRESULT rc;
10633
10634 // make a temporary list because detachDevice invalidates iterators into
10635 // mMediaData->mAttachments
10636 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10637
10638 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10639 it != llAttachments2.end();
10640 ++it)
10641 {
10642 ComObjPtr<MediumAttachment> &pAttach = *it;
10643 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10644
10645 if (!pMedium.isNull())
10646 {
10647 AutoCaller mac(pMedium);
10648 if (FAILED(mac.rc())) return mac.rc();
10649 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10650 DeviceType_T devType = pMedium->getDeviceType();
10651 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10652 && devType == DeviceType_HardDisk)
10653 || (cleanupMode == CleanupMode_Full)
10654 )
10655 {
10656 llMedia.push_back(pMedium);
10657 ComObjPtr<Medium> pParent = pMedium->getParent();
10658 /*
10659 * Search for medias which are not attached to any machine, but
10660 * in the chain to an attached disk. Mediums are only consided
10661 * if they are:
10662 * - have only one child
10663 * - no references to any machines
10664 * - are of normal medium type
10665 */
10666 while (!pParent.isNull())
10667 {
10668 AutoCaller mac1(pParent);
10669 if (FAILED(mac1.rc())) return mac1.rc();
10670 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10671 if (pParent->getChildren().size() == 1)
10672 {
10673 if ( pParent->getMachineBackRefCount() == 0
10674 && pParent->getType() == MediumType_Normal
10675 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10676 llMedia.push_back(pParent);
10677 }
10678 else
10679 break;
10680 pParent = pParent->getParent();
10681 }
10682 }
10683 }
10684
10685 // real machine: then we need to use the proper method
10686 rc = detachDevice(pAttach, writeLock, pSnapshot);
10687
10688 if (FAILED(rc))
10689 return rc;
10690 }
10691
10692 return S_OK;
10693}
10694
10695/**
10696 * Perform deferred hard disk detachments.
10697 *
10698 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10699 * backed up).
10700 *
10701 * If @a aOnline is @c true then this method will also unlock the old hard disks
10702 * for which the new implicit diffs were created and will lock these new diffs for
10703 * writing.
10704 *
10705 * @param aOnline Whether the VM was online prior to this operation.
10706 *
10707 * @note Locks this object for writing!
10708 */
10709void Machine::commitMedia(bool aOnline /*= false*/)
10710{
10711 AutoCaller autoCaller(this);
10712 AssertComRCReturnVoid(autoCaller.rc());
10713
10714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10715
10716 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10717
10718 HRESULT rc = S_OK;
10719
10720 /* no attach/detach operations -- nothing to do */
10721 if (!mMediaData.isBackedUp())
10722 return;
10723
10724 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10725 bool fMediaNeedsLocking = false;
10726
10727 /* enumerate new attachments */
10728 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10729 it != mMediaData->mAttachments.end();
10730 ++it)
10731 {
10732 MediumAttachment *pAttach = *it;
10733
10734 pAttach->commit();
10735
10736 Medium* pMedium = pAttach->getMedium();
10737 bool fImplicit = pAttach->isImplicit();
10738
10739 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10740 (pMedium) ? pMedium->getName().c_str() : "NULL",
10741 fImplicit));
10742
10743 /** @todo convert all this Machine-based voodoo to MediumAttachment
10744 * based commit logic. */
10745 if (fImplicit)
10746 {
10747 /* convert implicit attachment to normal */
10748 pAttach->setImplicit(false);
10749
10750 if ( aOnline
10751 && pMedium
10752 && pAttach->getType() == DeviceType_HardDisk
10753 )
10754 {
10755 ComObjPtr<Medium> parent = pMedium->getParent();
10756 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10757
10758 /* update the appropriate lock list */
10759 MediumLockList *pMediumLockList;
10760 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10761 AssertComRC(rc);
10762 if (pMediumLockList)
10763 {
10764 /* unlock if there's a need to change the locking */
10765 if (!fMediaNeedsLocking)
10766 {
10767 rc = mData->mSession.mLockedMedia.Unlock();
10768 AssertComRC(rc);
10769 fMediaNeedsLocking = true;
10770 }
10771 rc = pMediumLockList->Update(parent, false);
10772 AssertComRC(rc);
10773 rc = pMediumLockList->Append(pMedium, true);
10774 AssertComRC(rc);
10775 }
10776 }
10777
10778 continue;
10779 }
10780
10781 if (pMedium)
10782 {
10783 /* was this medium attached before? */
10784 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10785 oldIt != oldAtts.end();
10786 ++oldIt)
10787 {
10788 MediumAttachment *pOldAttach = *oldIt;
10789 if (pOldAttach->getMedium() == pMedium)
10790 {
10791 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10792
10793 /* yes: remove from old to avoid de-association */
10794 oldAtts.erase(oldIt);
10795 break;
10796 }
10797 }
10798 }
10799 }
10800
10801 /* enumerate remaining old attachments and de-associate from the
10802 * current machine state */
10803 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10804 it != oldAtts.end();
10805 ++it)
10806 {
10807 MediumAttachment *pAttach = *it;
10808 Medium* pMedium = pAttach->getMedium();
10809
10810 /* Detach only hard disks, since DVD/floppy media is detached
10811 * instantly in MountMedium. */
10812 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10813 {
10814 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10815
10816 /* now de-associate from the current machine state */
10817 rc = pMedium->removeBackReference(mData->mUuid);
10818 AssertComRC(rc);
10819
10820 if (aOnline)
10821 {
10822 /* unlock since medium is not used anymore */
10823 MediumLockList *pMediumLockList;
10824 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10825 AssertComRC(rc);
10826 if (pMediumLockList)
10827 {
10828 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10829 AssertComRC(rc);
10830 }
10831 }
10832 }
10833 }
10834
10835 /* take media locks again so that the locking state is consistent */
10836 if (fMediaNeedsLocking)
10837 {
10838 Assert(aOnline);
10839 rc = mData->mSession.mLockedMedia.Lock();
10840 AssertComRC(rc);
10841 }
10842
10843 /* commit the hard disk changes */
10844 mMediaData.commit();
10845
10846 if (isSessionMachine())
10847 {
10848 /*
10849 * Update the parent machine to point to the new owner.
10850 * This is necessary because the stored parent will point to the
10851 * session machine otherwise and cause crashes or errors later
10852 * when the session machine gets invalid.
10853 */
10854 /** @todo Change the MediumAttachment class to behave like any other
10855 * class in this regard by creating peer MediumAttachment
10856 * objects for session machines and share the data with the peer
10857 * machine.
10858 */
10859 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10860 it != mMediaData->mAttachments.end();
10861 ++it)
10862 {
10863 (*it)->updateParentMachine(mPeer);
10864 }
10865
10866 /* attach new data to the primary machine and reshare it */
10867 mPeer->mMediaData.attach(mMediaData);
10868 }
10869
10870 return;
10871}
10872
10873/**
10874 * Perform deferred deletion of implicitly created diffs.
10875 *
10876 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10877 * backed up).
10878 *
10879 * @note Locks this object for writing!
10880 */
10881void Machine::rollbackMedia()
10882{
10883 AutoCaller autoCaller(this);
10884 AssertComRCReturnVoid (autoCaller.rc());
10885
10886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10887
10888 LogFlowThisFunc(("Entering\n"));
10889
10890 HRESULT rc = S_OK;
10891
10892 /* no attach/detach operations -- nothing to do */
10893 if (!mMediaData.isBackedUp())
10894 return;
10895
10896 /* enumerate new attachments */
10897 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10898 it != mMediaData->mAttachments.end();
10899 ++it)
10900 {
10901 MediumAttachment *pAttach = *it;
10902 /* Fix up the backrefs for DVD/floppy media. */
10903 if (pAttach->getType() != DeviceType_HardDisk)
10904 {
10905 Medium* pMedium = pAttach->getMedium();
10906 if (pMedium)
10907 {
10908 rc = pMedium->removeBackReference(mData->mUuid);
10909 AssertComRC(rc);
10910 }
10911 }
10912
10913 (*it)->rollback();
10914
10915 pAttach = *it;
10916 /* Fix up the backrefs for DVD/floppy media. */
10917 if (pAttach->getType() != DeviceType_HardDisk)
10918 {
10919 Medium* pMedium = pAttach->getMedium();
10920 if (pMedium)
10921 {
10922 rc = pMedium->addBackReference(mData->mUuid);
10923 AssertComRC(rc);
10924 }
10925 }
10926 }
10927
10928 /** @todo convert all this Machine-based voodoo to MediumAttachment
10929 * based rollback logic. */
10930 deleteImplicitDiffs();
10931
10932 return;
10933}
10934
10935/**
10936 * Returns true if the settings file is located in the directory named exactly
10937 * as the machine; this means, among other things, that the machine directory
10938 * should be auto-renamed.
10939 *
10940 * @param aSettingsDir if not NULL, the full machine settings file directory
10941 * name will be assigned there.
10942 *
10943 * @note Doesn't lock anything.
10944 * @note Not thread safe (must be called from this object's lock).
10945 */
10946bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
10947{
10948 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10949 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
10950 if (aSettingsDir)
10951 *aSettingsDir = strMachineDirName;
10952 strMachineDirName.stripPath(); // vmname
10953 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
10954 strConfigFileOnly.stripPath() // vmname.vbox
10955 .stripExt(); // vmname
10956 /** @todo hack, make somehow use of ComposeMachineFilename */
10957 if (mUserData->s.fDirectoryIncludesUUID)
10958 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
10959
10960 AssertReturn(!strMachineDirName.isEmpty(), false);
10961 AssertReturn(!strConfigFileOnly.isEmpty(), false);
10962
10963 return strMachineDirName == strConfigFileOnly;
10964}
10965
10966/**
10967 * Discards all changes to machine settings.
10968 *
10969 * @param aNotify Whether to notify the direct session about changes or not.
10970 *
10971 * @note Locks objects for writing!
10972 */
10973void Machine::rollback(bool aNotify)
10974{
10975 AutoCaller autoCaller(this);
10976 AssertComRCReturn(autoCaller.rc(), (void)0);
10977
10978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10979
10980 if (!mStorageControllers.isNull())
10981 {
10982 if (mStorageControllers.isBackedUp())
10983 {
10984 /* unitialize all new devices (absent in the backed up list). */
10985 StorageControllerList::const_iterator it = mStorageControllers->begin();
10986 StorageControllerList *backedList = mStorageControllers.backedUpData();
10987 while (it != mStorageControllers->end())
10988 {
10989 if ( std::find(backedList->begin(), backedList->end(), *it)
10990 == backedList->end()
10991 )
10992 {
10993 (*it)->uninit();
10994 }
10995 ++it;
10996 }
10997
10998 /* restore the list */
10999 mStorageControllers.rollback();
11000 }
11001
11002 /* rollback any changes to devices after restoring the list */
11003 if (mData->flModifications & IsModified_Storage)
11004 {
11005 StorageControllerList::const_iterator it = mStorageControllers->begin();
11006 while (it != mStorageControllers->end())
11007 {
11008 (*it)->rollback();
11009 ++it;
11010 }
11011 }
11012 }
11013
11014 mUserData.rollback();
11015
11016 mHWData.rollback();
11017
11018 if (mData->flModifications & IsModified_Storage)
11019 rollbackMedia();
11020
11021 if (mBIOSSettings)
11022 mBIOSSettings->rollback();
11023
11024 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11025 mVRDEServer->rollback();
11026
11027 if (mAudioAdapter)
11028 mAudioAdapter->rollback();
11029
11030 if (mUSBController && (mData->flModifications & IsModified_USB))
11031 mUSBController->rollback();
11032
11033 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11034 mBandwidthControl->rollback();
11035
11036 if (!mHWData.isNull())
11037 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11038 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11039 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11040 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11041
11042 if (mData->flModifications & IsModified_NetworkAdapters)
11043 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11044 if ( mNetworkAdapters[slot]
11045 && mNetworkAdapters[slot]->isModified())
11046 {
11047 mNetworkAdapters[slot]->rollback();
11048 networkAdapters[slot] = mNetworkAdapters[slot];
11049 }
11050
11051 if (mData->flModifications & IsModified_SerialPorts)
11052 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11053 if ( mSerialPorts[slot]
11054 && mSerialPorts[slot]->isModified())
11055 {
11056 mSerialPorts[slot]->rollback();
11057 serialPorts[slot] = mSerialPorts[slot];
11058 }
11059
11060 if (mData->flModifications & IsModified_ParallelPorts)
11061 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11062 if ( mParallelPorts[slot]
11063 && mParallelPorts[slot]->isModified())
11064 {
11065 mParallelPorts[slot]->rollback();
11066 parallelPorts[slot] = mParallelPorts[slot];
11067 }
11068
11069 if (aNotify)
11070 {
11071 /* inform the direct session about changes */
11072
11073 ComObjPtr<Machine> that = this;
11074 uint32_t flModifications = mData->flModifications;
11075 alock.release();
11076
11077 if (flModifications & IsModified_SharedFolders)
11078 that->onSharedFolderChange();
11079
11080 if (flModifications & IsModified_VRDEServer)
11081 that->onVRDEServerChange(/* aRestart */ TRUE);
11082 if (flModifications & IsModified_USB)
11083 that->onUSBControllerChange();
11084
11085 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11086 if (networkAdapters[slot])
11087 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11088 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11089 if (serialPorts[slot])
11090 that->onSerialPortChange(serialPorts[slot]);
11091 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11092 if (parallelPorts[slot])
11093 that->onParallelPortChange(parallelPorts[slot]);
11094
11095 if (flModifications & IsModified_Storage)
11096 that->onStorageControllerChange();
11097
11098#if 0
11099 if (flModifications & IsModified_BandwidthControl)
11100 that->onBandwidthControlChange();
11101#endif
11102 }
11103}
11104
11105/**
11106 * Commits all the changes to machine settings.
11107 *
11108 * Note that this operation is supposed to never fail.
11109 *
11110 * @note Locks this object and children for writing.
11111 */
11112void Machine::commit()
11113{
11114 AutoCaller autoCaller(this);
11115 AssertComRCReturnVoid(autoCaller.rc());
11116
11117 AutoCaller peerCaller(mPeer);
11118 AssertComRCReturnVoid(peerCaller.rc());
11119
11120 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11121
11122 /*
11123 * use safe commit to ensure Snapshot machines (that share mUserData)
11124 * will still refer to a valid memory location
11125 */
11126 mUserData.commitCopy();
11127
11128 mHWData.commit();
11129
11130 if (mMediaData.isBackedUp())
11131 commitMedia();
11132
11133 mBIOSSettings->commit();
11134 mVRDEServer->commit();
11135 mAudioAdapter->commit();
11136 mUSBController->commit();
11137 mBandwidthControl->commit();
11138
11139 /* Since mNetworkAdapters is a list which might have been changed (resized)
11140 * without using the Backupable<> template we need to handle the copying
11141 * of the list entries manually, including the creation of peers for the
11142 * new objects. */
11143 bool commitNetworkAdapters = false;
11144 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11145 if (mPeer)
11146 {
11147 /* commit everything, even the ones which will go away */
11148 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11149 mNetworkAdapters[slot]->commit();
11150 /* copy over the new entries, creating a peer and uninit the original */
11151 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11152 for (size_t slot = 0; slot < newSize; slot++)
11153 {
11154 /* look if this adapter has a peer device */
11155 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11156 if (!peer)
11157 {
11158 /* no peer means the adapter is a newly created one;
11159 * create a peer owning data this data share it with */
11160 peer.createObject();
11161 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11162 }
11163 mPeer->mNetworkAdapters[slot] = peer;
11164 }
11165 /* uninit any no longer needed network adapters */
11166 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11167 mNetworkAdapters[slot]->uninit();
11168 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11169 {
11170 if (mPeer->mNetworkAdapters[slot])
11171 mPeer->mNetworkAdapters[slot]->uninit();
11172 }
11173 /* Keep the original network adapter count until this point, so that
11174 * discarding a chipset type change will not lose settings. */
11175 mNetworkAdapters.resize(newSize);
11176 mPeer->mNetworkAdapters.resize(newSize);
11177 }
11178 else
11179 {
11180 /* we have no peer (our parent is the newly created machine);
11181 * just commit changes to the network adapters */
11182 commitNetworkAdapters = true;
11183 }
11184 if (commitNetworkAdapters)
11185 {
11186 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11187 mNetworkAdapters[slot]->commit();
11188 }
11189
11190 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11191 mSerialPorts[slot]->commit();
11192 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11193 mParallelPorts[slot]->commit();
11194
11195 bool commitStorageControllers = false;
11196
11197 if (mStorageControllers.isBackedUp())
11198 {
11199 mStorageControllers.commit();
11200
11201 if (mPeer)
11202 {
11203 /* Commit all changes to new controllers (this will reshare data with
11204 * peers for those who have peers) */
11205 StorageControllerList *newList = new StorageControllerList();
11206 StorageControllerList::const_iterator it = mStorageControllers->begin();
11207 while (it != mStorageControllers->end())
11208 {
11209 (*it)->commit();
11210
11211 /* look if this controller has a peer device */
11212 ComObjPtr<StorageController> peer = (*it)->getPeer();
11213 if (!peer)
11214 {
11215 /* no peer means the device is a newly created one;
11216 * create a peer owning data this device share it with */
11217 peer.createObject();
11218 peer->init(mPeer, *it, true /* aReshare */);
11219 }
11220 else
11221 {
11222 /* remove peer from the old list */
11223 mPeer->mStorageControllers->remove(peer);
11224 }
11225 /* and add it to the new list */
11226 newList->push_back(peer);
11227
11228 ++it;
11229 }
11230
11231 /* uninit old peer's controllers that are left */
11232 it = mPeer->mStorageControllers->begin();
11233 while (it != mPeer->mStorageControllers->end())
11234 {
11235 (*it)->uninit();
11236 ++it;
11237 }
11238
11239 /* attach new list of controllers to our peer */
11240 mPeer->mStorageControllers.attach(newList);
11241 }
11242 else
11243 {
11244 /* we have no peer (our parent is the newly created machine);
11245 * just commit changes to devices */
11246 commitStorageControllers = true;
11247 }
11248 }
11249 else
11250 {
11251 /* the list of controllers itself is not changed,
11252 * just commit changes to controllers themselves */
11253 commitStorageControllers = true;
11254 }
11255
11256 if (commitStorageControllers)
11257 {
11258 StorageControllerList::const_iterator it = mStorageControllers->begin();
11259 while (it != mStorageControllers->end())
11260 {
11261 (*it)->commit();
11262 ++it;
11263 }
11264 }
11265
11266 if (isSessionMachine())
11267 {
11268 /* attach new data to the primary machine and reshare it */
11269 mPeer->mUserData.attach(mUserData);
11270 mPeer->mHWData.attach(mHWData);
11271 /* mMediaData is reshared by fixupMedia */
11272 // mPeer->mMediaData.attach(mMediaData);
11273 Assert(mPeer->mMediaData.data() == mMediaData.data());
11274 }
11275}
11276
11277/**
11278 * Copies all the hardware data from the given machine.
11279 *
11280 * Currently, only called when the VM is being restored from a snapshot. In
11281 * particular, this implies that the VM is not running during this method's
11282 * call.
11283 *
11284 * @note This method must be called from under this object's lock.
11285 *
11286 * @note This method doesn't call #commit(), so all data remains backed up and
11287 * unsaved.
11288 */
11289void Machine::copyFrom(Machine *aThat)
11290{
11291 AssertReturnVoid(!isSnapshotMachine());
11292 AssertReturnVoid(aThat->isSnapshotMachine());
11293
11294 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11295
11296 mHWData.assignCopy(aThat->mHWData);
11297
11298 // create copies of all shared folders (mHWData after attaching a copy
11299 // contains just references to original objects)
11300 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11301 it != mHWData->mSharedFolders.end();
11302 ++it)
11303 {
11304 ComObjPtr<SharedFolder> folder;
11305 folder.createObject();
11306 HRESULT rc = folder->initCopy(getMachine(), *it);
11307 AssertComRC(rc);
11308 *it = folder;
11309 }
11310
11311 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11312 mVRDEServer->copyFrom(aThat->mVRDEServer);
11313 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11314 mUSBController->copyFrom(aThat->mUSBController);
11315 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11316
11317 /* create private copies of all controllers */
11318 mStorageControllers.backup();
11319 mStorageControllers->clear();
11320 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11321 it != aThat->mStorageControllers->end();
11322 ++it)
11323 {
11324 ComObjPtr<StorageController> ctrl;
11325 ctrl.createObject();
11326 ctrl->initCopy(this, *it);
11327 mStorageControllers->push_back(ctrl);
11328 }
11329
11330 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11331 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11332 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11333 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11334 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11335 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11336 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11337}
11338
11339/**
11340 * Returns whether the given storage controller is hotplug capable.
11341 *
11342 * @returns true if the controller supports hotplugging
11343 * false otherwise.
11344 * @param enmCtrlType The controller type to check for.
11345 */
11346bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11347{
11348 switch (enmCtrlType)
11349 {
11350 case StorageControllerType_IntelAhci:
11351 return true;
11352 case StorageControllerType_LsiLogic:
11353 case StorageControllerType_LsiLogicSas:
11354 case StorageControllerType_BusLogic:
11355 case StorageControllerType_PIIX3:
11356 case StorageControllerType_PIIX4:
11357 case StorageControllerType_ICH6:
11358 case StorageControllerType_I82078:
11359 default:
11360 return false;
11361 }
11362}
11363
11364#ifdef VBOX_WITH_RESOURCE_USAGE_API
11365
11366void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11367{
11368 AssertReturnVoid(isWriteLockOnCurrentThread());
11369 AssertPtrReturnVoid(aCollector);
11370
11371 pm::CollectorHAL *hal = aCollector->getHAL();
11372 /* Create sub metrics */
11373 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11374 "Percentage of processor time spent in user mode by the VM process.");
11375 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11376 "Percentage of processor time spent in kernel mode by the VM process.");
11377 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11378 "Size of resident portion of VM process in memory.");
11379 /* Create and register base metrics */
11380 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11381 cpuLoadUser, cpuLoadKernel);
11382 aCollector->registerBaseMetric(cpuLoad);
11383 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11384 ramUsageUsed);
11385 aCollector->registerBaseMetric(ramUsage);
11386
11387 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11388 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11389 new pm::AggregateAvg()));
11390 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11391 new pm::AggregateMin()));
11392 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11393 new pm::AggregateMax()));
11394 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11395 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11396 new pm::AggregateAvg()));
11397 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11398 new pm::AggregateMin()));
11399 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11400 new pm::AggregateMax()));
11401
11402 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11403 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11404 new pm::AggregateAvg()));
11405 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11406 new pm::AggregateMin()));
11407 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11408 new pm::AggregateMax()));
11409
11410
11411 /* Guest metrics collector */
11412 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11413 aCollector->registerGuest(mCollectorGuest);
11414 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11415 this, __PRETTY_FUNCTION__, mCollectorGuest));
11416
11417 /* Create sub metrics */
11418 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11419 "Percentage of processor time spent in user mode as seen by the guest.");
11420 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11421 "Percentage of processor time spent in kernel mode as seen by the guest.");
11422 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11423 "Percentage of processor time spent idling as seen by the guest.");
11424
11425 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11426 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11427 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11428 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11429 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11430 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11431
11432 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11433
11434 /* Create and register base metrics */
11435 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11436 guestLoadUser, guestLoadKernel, guestLoadIdle);
11437 aCollector->registerBaseMetric(guestCpuLoad);
11438
11439 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11440 guestMemTotal, guestMemFree,
11441 guestMemBalloon, guestMemShared,
11442 guestMemCache, guestPagedTotal);
11443 aCollector->registerBaseMetric(guestCpuMem);
11444
11445 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11446 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11447 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11448 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11449
11450 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11451 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11452 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11453 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11454
11455 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11456 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11457 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11458 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11459
11460 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11461 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11462 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11463 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11464
11465 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11466 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11467 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11468 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11469
11470 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11471 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11472 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11473 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11474
11475 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11476 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11477 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11478 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11479
11480 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11481 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11482 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11483 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11484
11485 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11486 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11487 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11488 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11489}
11490
11491void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11492{
11493 AssertReturnVoid(isWriteLockOnCurrentThread());
11494
11495 if (aCollector)
11496 {
11497 aCollector->unregisterMetricsFor(aMachine);
11498 aCollector->unregisterBaseMetricsFor(aMachine);
11499 }
11500}
11501
11502#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11503
11504
11505////////////////////////////////////////////////////////////////////////////////
11506
11507DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11508
11509HRESULT SessionMachine::FinalConstruct()
11510{
11511 LogFlowThisFunc(("\n"));
11512
11513#if defined(RT_OS_WINDOWS)
11514 mIPCSem = NULL;
11515#elif defined(RT_OS_OS2)
11516 mIPCSem = NULLHANDLE;
11517#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11518 mIPCSem = -1;
11519#else
11520# error "Port me!"
11521#endif
11522
11523 return BaseFinalConstruct();
11524}
11525
11526void SessionMachine::FinalRelease()
11527{
11528 LogFlowThisFunc(("\n"));
11529
11530 uninit(Uninit::Unexpected);
11531
11532 BaseFinalRelease();
11533}
11534
11535/**
11536 * @note Must be called only by Machine::openSession() from its own write lock.
11537 */
11538HRESULT SessionMachine::init(Machine *aMachine)
11539{
11540 LogFlowThisFuncEnter();
11541 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11542
11543 AssertReturn(aMachine, E_INVALIDARG);
11544
11545 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11546
11547 /* Enclose the state transition NotReady->InInit->Ready */
11548 AutoInitSpan autoInitSpan(this);
11549 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11550
11551 /* create the interprocess semaphore */
11552#if defined(RT_OS_WINDOWS)
11553 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11554 for (size_t i = 0; i < mIPCSemName.length(); i++)
11555 if (mIPCSemName.raw()[i] == '\\')
11556 mIPCSemName.raw()[i] = '/';
11557 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11558 ComAssertMsgRet(mIPCSem,
11559 ("Cannot create IPC mutex '%ls', err=%d",
11560 mIPCSemName.raw(), ::GetLastError()),
11561 E_FAIL);
11562#elif defined(RT_OS_OS2)
11563 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11564 aMachine->mData->mUuid.raw());
11565 mIPCSemName = ipcSem;
11566 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11567 ComAssertMsgRet(arc == NO_ERROR,
11568 ("Cannot create IPC mutex '%s', arc=%ld",
11569 ipcSem.c_str(), arc),
11570 E_FAIL);
11571#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11572# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11573# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11574 /** @todo Check that this still works correctly. */
11575 AssertCompileSize(key_t, 8);
11576# else
11577 AssertCompileSize(key_t, 4);
11578# endif
11579 key_t key;
11580 mIPCSem = -1;
11581 mIPCKey = "0";
11582 for (uint32_t i = 0; i < 1 << 24; i++)
11583 {
11584 key = ((uint32_t)'V' << 24) | i;
11585 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11586 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11587 {
11588 mIPCSem = sem;
11589 if (sem >= 0)
11590 mIPCKey = BstrFmt("%u", key);
11591 break;
11592 }
11593 }
11594# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11595 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11596 char *pszSemName = NULL;
11597 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11598 key_t key = ::ftok(pszSemName, 'V');
11599 RTStrFree(pszSemName);
11600
11601 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11602# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11603
11604 int errnoSave = errno;
11605 if (mIPCSem < 0 && errnoSave == ENOSYS)
11606 {
11607 setError(E_FAIL,
11608 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11609 "support for SysV IPC. Check the host kernel configuration for "
11610 "CONFIG_SYSVIPC=y"));
11611 return E_FAIL;
11612 }
11613 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11614 * the IPC semaphores */
11615 if (mIPCSem < 0 && errnoSave == ENOSPC)
11616 {
11617#ifdef RT_OS_LINUX
11618 setError(E_FAIL,
11619 tr("Cannot create IPC semaphore because the system limit for the "
11620 "maximum number of semaphore sets (SEMMNI), or the system wide "
11621 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11622 "current set of SysV IPC semaphores can be determined from "
11623 "the file /proc/sysvipc/sem"));
11624#else
11625 setError(E_FAIL,
11626 tr("Cannot create IPC semaphore because the system-imposed limit "
11627 "on the maximum number of allowed semaphores or semaphore "
11628 "identifiers system-wide would be exceeded"));
11629#endif
11630 return E_FAIL;
11631 }
11632 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11633 E_FAIL);
11634 /* set the initial value to 1 */
11635 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11636 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11637 E_FAIL);
11638#else
11639# error "Port me!"
11640#endif
11641
11642 /* memorize the peer Machine */
11643 unconst(mPeer) = aMachine;
11644 /* share the parent pointer */
11645 unconst(mParent) = aMachine->mParent;
11646
11647 /* take the pointers to data to share */
11648 mData.share(aMachine->mData);
11649 mSSData.share(aMachine->mSSData);
11650
11651 mUserData.share(aMachine->mUserData);
11652 mHWData.share(aMachine->mHWData);
11653 mMediaData.share(aMachine->mMediaData);
11654
11655 mStorageControllers.allocate();
11656 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11657 it != aMachine->mStorageControllers->end();
11658 ++it)
11659 {
11660 ComObjPtr<StorageController> ctl;
11661 ctl.createObject();
11662 ctl->init(this, *it);
11663 mStorageControllers->push_back(ctl);
11664 }
11665
11666 unconst(mBIOSSettings).createObject();
11667 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11668 /* create another VRDEServer object that will be mutable */
11669 unconst(mVRDEServer).createObject();
11670 mVRDEServer->init(this, aMachine->mVRDEServer);
11671 /* create another audio adapter object that will be mutable */
11672 unconst(mAudioAdapter).createObject();
11673 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11674 /* create a list of serial ports that will be mutable */
11675 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11676 {
11677 unconst(mSerialPorts[slot]).createObject();
11678 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11679 }
11680 /* create a list of parallel ports that will be mutable */
11681 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11682 {
11683 unconst(mParallelPorts[slot]).createObject();
11684 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11685 }
11686 /* create another USB controller object that will be mutable */
11687 unconst(mUSBController).createObject();
11688 mUSBController->init(this, aMachine->mUSBController);
11689
11690 /* create a list of network adapters that will be mutable */
11691 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11692 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11693 {
11694 unconst(mNetworkAdapters[slot]).createObject();
11695 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11696 }
11697
11698 /* create another bandwidth control object that will be mutable */
11699 unconst(mBandwidthControl).createObject();
11700 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11701
11702 /* default is to delete saved state on Saved -> PoweredOff transition */
11703 mRemoveSavedState = true;
11704
11705 /* Confirm a successful initialization when it's the case */
11706 autoInitSpan.setSucceeded();
11707
11708 LogFlowThisFuncLeave();
11709 return S_OK;
11710}
11711
11712/**
11713 * Uninitializes this session object. If the reason is other than
11714 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11715 *
11716 * @param aReason uninitialization reason
11717 *
11718 * @note Locks mParent + this object for writing.
11719 */
11720void SessionMachine::uninit(Uninit::Reason aReason)
11721{
11722 LogFlowThisFuncEnter();
11723 LogFlowThisFunc(("reason=%d\n", aReason));
11724
11725 /*
11726 * Strongly reference ourselves to prevent this object deletion after
11727 * mData->mSession.mMachine.setNull() below (which can release the last
11728 * reference and call the destructor). Important: this must be done before
11729 * accessing any members (and before AutoUninitSpan that does it as well).
11730 * This self reference will be released as the very last step on return.
11731 */
11732 ComObjPtr<SessionMachine> selfRef = this;
11733
11734 /* Enclose the state transition Ready->InUninit->NotReady */
11735 AutoUninitSpan autoUninitSpan(this);
11736 if (autoUninitSpan.uninitDone())
11737 {
11738 LogFlowThisFunc(("Already uninitialized\n"));
11739 LogFlowThisFuncLeave();
11740 return;
11741 }
11742
11743 if (autoUninitSpan.initFailed())
11744 {
11745 /* We've been called by init() because it's failed. It's not really
11746 * necessary (nor it's safe) to perform the regular uninit sequence
11747 * below, the following is enough.
11748 */
11749 LogFlowThisFunc(("Initialization failed.\n"));
11750#if defined(RT_OS_WINDOWS)
11751 if (mIPCSem)
11752 ::CloseHandle(mIPCSem);
11753 mIPCSem = NULL;
11754#elif defined(RT_OS_OS2)
11755 if (mIPCSem != NULLHANDLE)
11756 ::DosCloseMutexSem(mIPCSem);
11757 mIPCSem = NULLHANDLE;
11758#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11759 if (mIPCSem >= 0)
11760 ::semctl(mIPCSem, 0, IPC_RMID);
11761 mIPCSem = -1;
11762# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11763 mIPCKey = "0";
11764# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11765#else
11766# error "Port me!"
11767#endif
11768 uninitDataAndChildObjects();
11769 mData.free();
11770 unconst(mParent) = NULL;
11771 unconst(mPeer) = NULL;
11772 LogFlowThisFuncLeave();
11773 return;
11774 }
11775
11776 MachineState_T lastState;
11777 {
11778 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11779 lastState = mData->mMachineState;
11780 }
11781 NOREF(lastState);
11782
11783#ifdef VBOX_WITH_USB
11784 // release all captured USB devices, but do this before requesting the locks below
11785 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11786 {
11787 /* Console::captureUSBDevices() is called in the VM process only after
11788 * setting the machine state to Starting or Restoring.
11789 * Console::detachAllUSBDevices() will be called upon successful
11790 * termination. So, we need to release USB devices only if there was
11791 * an abnormal termination of a running VM.
11792 *
11793 * This is identical to SessionMachine::DetachAllUSBDevices except
11794 * for the aAbnormal argument. */
11795 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11796 AssertComRC(rc);
11797 NOREF(rc);
11798
11799 USBProxyService *service = mParent->host()->usbProxyService();
11800 if (service)
11801 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11802 }
11803#endif /* VBOX_WITH_USB */
11804
11805 // we need to lock this object in uninit() because the lock is shared
11806 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11807 // and others need mParent lock, and USB needs host lock.
11808 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11809
11810#if 0
11811 // Trigger async cleanup tasks, avoid doing things here which are not
11812 // vital to be done immediately and maybe need more locks. This calls
11813 // Machine::unregisterMetrics().
11814 mParent->onMachineUninit(mPeer);
11815#else
11816 /*
11817 * It is safe to call Machine::unregisterMetrics() here because
11818 * PerformanceCollector::samplerCallback no longer accesses guest methods
11819 * holding the lock.
11820 */
11821 unregisterMetrics(mParent->performanceCollector(), mPeer);
11822#endif
11823 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11824 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11825 this, __PRETTY_FUNCTION__, mCollectorGuest));
11826 if (mCollectorGuest)
11827 {
11828 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11829 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11830 mCollectorGuest = NULL;
11831 }
11832
11833 if (aReason == Uninit::Abnormal)
11834 {
11835 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11836 Global::IsOnlineOrTransient(lastState)));
11837
11838 /* reset the state to Aborted */
11839 if (mData->mMachineState != MachineState_Aborted)
11840 setMachineState(MachineState_Aborted);
11841 }
11842
11843 // any machine settings modified?
11844 if (mData->flModifications)
11845 {
11846 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11847 rollback(false /* aNotify */);
11848 }
11849
11850 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11851 || !mConsoleTaskData.mSnapshot);
11852 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11853 {
11854 LogWarningThisFunc(("canceling failed save state request!\n"));
11855 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11856 }
11857 else if (!mConsoleTaskData.mSnapshot.isNull())
11858 {
11859 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11860
11861 /* delete all differencing hard disks created (this will also attach
11862 * their parents back by rolling back mMediaData) */
11863 rollbackMedia();
11864
11865 // delete the saved state file (it might have been already created)
11866 // AFTER killing the snapshot so that releaseSavedStateFile() won't
11867 // think it's still in use
11868 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
11869 mConsoleTaskData.mSnapshot->uninit();
11870 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
11871 }
11872
11873 if (!mData->mSession.mType.isEmpty())
11874 {
11875 /* mType is not null when this machine's process has been started by
11876 * Machine::LaunchVMProcess(), therefore it is our child. We
11877 * need to queue the PID to reap the process (and avoid zombies on
11878 * Linux). */
11879 Assert(mData->mSession.mPID != NIL_RTPROCESS);
11880 mParent->addProcessToReap(mData->mSession.mPID);
11881 }
11882
11883 mData->mSession.mPID = NIL_RTPROCESS;
11884
11885 if (aReason == Uninit::Unexpected)
11886 {
11887 /* Uninitialization didn't come from #checkForDeath(), so tell the
11888 * client watcher thread to update the set of machines that have open
11889 * sessions. */
11890 mParent->updateClientWatcher();
11891 }
11892
11893 /* uninitialize all remote controls */
11894 if (mData->mSession.mRemoteControls.size())
11895 {
11896 LogFlowThisFunc(("Closing remote sessions (%d):\n",
11897 mData->mSession.mRemoteControls.size()));
11898
11899 Data::Session::RemoteControlList::iterator it =
11900 mData->mSession.mRemoteControls.begin();
11901 while (it != mData->mSession.mRemoteControls.end())
11902 {
11903 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
11904 HRESULT rc = (*it)->Uninitialize();
11905 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
11906 if (FAILED(rc))
11907 LogWarningThisFunc(("Forgot to close the remote session?\n"));
11908 ++it;
11909 }
11910 mData->mSession.mRemoteControls.clear();
11911 }
11912
11913 /*
11914 * An expected uninitialization can come only from #checkForDeath().
11915 * Otherwise it means that something's gone really wrong (for example,
11916 * the Session implementation has released the VirtualBox reference
11917 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
11918 * etc). However, it's also possible, that the client releases the IPC
11919 * semaphore correctly (i.e. before it releases the VirtualBox reference),
11920 * but the VirtualBox release event comes first to the server process.
11921 * This case is practically possible, so we should not assert on an
11922 * unexpected uninit, just log a warning.
11923 */
11924
11925 if ((aReason == Uninit::Unexpected))
11926 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
11927
11928 if (aReason != Uninit::Normal)
11929 {
11930 mData->mSession.mDirectControl.setNull();
11931 }
11932 else
11933 {
11934 /* this must be null here (see #OnSessionEnd()) */
11935 Assert(mData->mSession.mDirectControl.isNull());
11936 Assert(mData->mSession.mState == SessionState_Unlocking);
11937 Assert(!mData->mSession.mProgress.isNull());
11938 }
11939 if (mData->mSession.mProgress)
11940 {
11941 if (aReason == Uninit::Normal)
11942 mData->mSession.mProgress->notifyComplete(S_OK);
11943 else
11944 mData->mSession.mProgress->notifyComplete(E_FAIL,
11945 COM_IIDOF(ISession),
11946 getComponentName(),
11947 tr("The VM session was aborted"));
11948 mData->mSession.mProgress.setNull();
11949 }
11950
11951 /* remove the association between the peer machine and this session machine */
11952 Assert( (SessionMachine*)mData->mSession.mMachine == this
11953 || aReason == Uninit::Unexpected);
11954
11955 /* reset the rest of session data */
11956 mData->mSession.mMachine.setNull();
11957 mData->mSession.mState = SessionState_Unlocked;
11958 mData->mSession.mType.setNull();
11959
11960 /* close the interprocess semaphore before leaving the exclusive lock */
11961#if defined(RT_OS_WINDOWS)
11962 if (mIPCSem)
11963 ::CloseHandle(mIPCSem);
11964 mIPCSem = NULL;
11965#elif defined(RT_OS_OS2)
11966 if (mIPCSem != NULLHANDLE)
11967 ::DosCloseMutexSem(mIPCSem);
11968 mIPCSem = NULLHANDLE;
11969#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11970 if (mIPCSem >= 0)
11971 ::semctl(mIPCSem, 0, IPC_RMID);
11972 mIPCSem = -1;
11973# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11974 mIPCKey = "0";
11975# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11976#else
11977# error "Port me!"
11978#endif
11979
11980 /* fire an event */
11981 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
11982
11983 uninitDataAndChildObjects();
11984
11985 /* free the essential data structure last */
11986 mData.free();
11987
11988 /* release the exclusive lock before setting the below two to NULL */
11989 multilock.release();
11990
11991 unconst(mParent) = NULL;
11992 unconst(mPeer) = NULL;
11993
11994 LogFlowThisFuncLeave();
11995}
11996
11997// util::Lockable interface
11998////////////////////////////////////////////////////////////////////////////////
11999
12000/**
12001 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12002 * with the primary Machine instance (mPeer).
12003 */
12004RWLockHandle *SessionMachine::lockHandle() const
12005{
12006 AssertReturn(mPeer != NULL, NULL);
12007 return mPeer->lockHandle();
12008}
12009
12010// IInternalMachineControl methods
12011////////////////////////////////////////////////////////////////////////////////
12012
12013/**
12014 * Passes collected guest statistics to performance collector object
12015 */
12016STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
12017 ULONG aCpuKernel, ULONG aCpuIdle,
12018 ULONG aMemTotal, ULONG aMemFree,
12019 ULONG aMemBalloon, ULONG aMemShared,
12020 ULONG aMemCache, ULONG aPageTotal,
12021 ULONG aAllocVMM, ULONG aFreeVMM,
12022 ULONG aBalloonedVMM, ULONG aSharedVMM)
12023{
12024 if (mCollectorGuest)
12025 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12026 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12027 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12028 aBalloonedVMM, aSharedVMM);
12029
12030 return S_OK;
12031}
12032
12033/**
12034 * @note Locks this object for writing.
12035 */
12036STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12037{
12038 AutoCaller autoCaller(this);
12039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12040
12041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12042
12043 mRemoveSavedState = aRemove;
12044
12045 return S_OK;
12046}
12047
12048/**
12049 * @note Locks the same as #setMachineState() does.
12050 */
12051STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12052{
12053 return setMachineState(aMachineState);
12054}
12055
12056/**
12057 * @note Locks this object for reading.
12058 */
12059STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12060{
12061 AutoCaller autoCaller(this);
12062 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12063
12064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12065
12066#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12067 mIPCSemName.cloneTo(aId);
12068 return S_OK;
12069#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12070# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12071 mIPCKey.cloneTo(aId);
12072# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12073 mData->m_strConfigFileFull.cloneTo(aId);
12074# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12075 return S_OK;
12076#else
12077# error "Port me!"
12078#endif
12079}
12080
12081/**
12082 * @note Locks this object for writing.
12083 */
12084STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12085{
12086 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12087 AutoCaller autoCaller(this);
12088 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12089
12090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12091
12092 if (mData->mSession.mState != SessionState_Locked)
12093 return VBOX_E_INVALID_OBJECT_STATE;
12094
12095 if (!mData->mSession.mProgress.isNull())
12096 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12097
12098 LogFlowThisFunc(("returns S_OK.\n"));
12099 return S_OK;
12100}
12101
12102/**
12103 * @note Locks this object for writing.
12104 */
12105STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12106{
12107 AutoCaller autoCaller(this);
12108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12109
12110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12111
12112 if (mData->mSession.mState != SessionState_Locked)
12113 return VBOX_E_INVALID_OBJECT_STATE;
12114
12115 /* Finalize the LaunchVMProcess progress object. */
12116 if (mData->mSession.mProgress)
12117 {
12118 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12119 mData->mSession.mProgress.setNull();
12120 }
12121
12122 if (SUCCEEDED((HRESULT)iResult))
12123 {
12124#ifdef VBOX_WITH_RESOURCE_USAGE_API
12125 /* The VM has been powered up successfully, so it makes sense
12126 * now to offer the performance metrics for a running machine
12127 * object. Doing it earlier wouldn't be safe. */
12128 registerMetrics(mParent->performanceCollector(), mPeer,
12129 mData->mSession.mPID);
12130#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12131 }
12132
12133 return S_OK;
12134}
12135
12136/**
12137 * @note Locks this object for writing.
12138 */
12139STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12140{
12141 LogFlowThisFuncEnter();
12142
12143 CheckComArgOutPointerValid(aProgress);
12144
12145 AutoCaller autoCaller(this);
12146 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12147
12148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12149
12150 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12151 E_FAIL);
12152
12153 /* create a progress object to track operation completion */
12154 ComObjPtr<Progress> pProgress;
12155 pProgress.createObject();
12156 pProgress->init(getVirtualBox(),
12157 static_cast<IMachine *>(this) /* aInitiator */,
12158 Bstr(tr("Stopping the virtual machine")).raw(),
12159 FALSE /* aCancelable */);
12160
12161 /* fill in the console task data */
12162 mConsoleTaskData.mLastState = mData->mMachineState;
12163 mConsoleTaskData.mProgress = pProgress;
12164
12165 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12166 setMachineState(MachineState_Stopping);
12167
12168 pProgress.queryInterfaceTo(aProgress);
12169
12170 return S_OK;
12171}
12172
12173/**
12174 * @note Locks this object for writing.
12175 */
12176STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12177{
12178 LogFlowThisFuncEnter();
12179
12180 AutoCaller autoCaller(this);
12181 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12182
12183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12184
12185 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12186 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12187 && mConsoleTaskData.mLastState != MachineState_Null,
12188 E_FAIL);
12189
12190 /*
12191 * On failure, set the state to the state we had when BeginPoweringDown()
12192 * was called (this is expected by Console::PowerDown() and the associated
12193 * task). On success the VM process already changed the state to
12194 * MachineState_PoweredOff, so no need to do anything.
12195 */
12196 if (FAILED(iResult))
12197 setMachineState(mConsoleTaskData.mLastState);
12198
12199 /* notify the progress object about operation completion */
12200 Assert(mConsoleTaskData.mProgress);
12201 if (SUCCEEDED(iResult))
12202 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12203 else
12204 {
12205 Utf8Str strErrMsg(aErrMsg);
12206 if (strErrMsg.length())
12207 mConsoleTaskData.mProgress->notifyComplete(iResult,
12208 COM_IIDOF(ISession),
12209 getComponentName(),
12210 strErrMsg.c_str());
12211 else
12212 mConsoleTaskData.mProgress->notifyComplete(iResult);
12213 }
12214
12215 /* clear out the temporary saved state data */
12216 mConsoleTaskData.mLastState = MachineState_Null;
12217 mConsoleTaskData.mProgress.setNull();
12218
12219 LogFlowThisFuncLeave();
12220 return S_OK;
12221}
12222
12223
12224/**
12225 * Goes through the USB filters of the given machine to see if the given
12226 * device matches any filter or not.
12227 *
12228 * @note Locks the same as USBController::hasMatchingFilter() does.
12229 */
12230STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12231 BOOL *aMatched,
12232 ULONG *aMaskedIfs)
12233{
12234 LogFlowThisFunc(("\n"));
12235
12236 CheckComArgNotNull(aUSBDevice);
12237 CheckComArgOutPointerValid(aMatched);
12238
12239 AutoCaller autoCaller(this);
12240 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12241
12242#ifdef VBOX_WITH_USB
12243 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12244#else
12245 NOREF(aUSBDevice);
12246 NOREF(aMaskedIfs);
12247 *aMatched = FALSE;
12248#endif
12249
12250 return S_OK;
12251}
12252
12253/**
12254 * @note Locks the same as Host::captureUSBDevice() does.
12255 */
12256STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12257{
12258 LogFlowThisFunc(("\n"));
12259
12260 AutoCaller autoCaller(this);
12261 AssertComRCReturnRC(autoCaller.rc());
12262
12263#ifdef VBOX_WITH_USB
12264 /* if captureDeviceForVM() fails, it must have set extended error info */
12265 clearError();
12266 MultiResult rc = mParent->host()->checkUSBProxyService();
12267 if (FAILED(rc)) return rc;
12268
12269 USBProxyService *service = mParent->host()->usbProxyService();
12270 AssertReturn(service, E_FAIL);
12271 return service->captureDeviceForVM(this, Guid(aId).ref());
12272#else
12273 NOREF(aId);
12274 return E_NOTIMPL;
12275#endif
12276}
12277
12278/**
12279 * @note Locks the same as Host::detachUSBDevice() does.
12280 */
12281STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12282{
12283 LogFlowThisFunc(("\n"));
12284
12285 AutoCaller autoCaller(this);
12286 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12287
12288#ifdef VBOX_WITH_USB
12289 USBProxyService *service = mParent->host()->usbProxyService();
12290 AssertReturn(service, E_FAIL);
12291 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12292#else
12293 NOREF(aId);
12294 NOREF(aDone);
12295 return E_NOTIMPL;
12296#endif
12297}
12298
12299/**
12300 * Inserts all machine filters to the USB proxy service and then calls
12301 * Host::autoCaptureUSBDevices().
12302 *
12303 * Called by Console from the VM process upon VM startup.
12304 *
12305 * @note Locks what called methods lock.
12306 */
12307STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12308{
12309 LogFlowThisFunc(("\n"));
12310
12311 AutoCaller autoCaller(this);
12312 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12313
12314#ifdef VBOX_WITH_USB
12315 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12316 AssertComRC(rc);
12317 NOREF(rc);
12318
12319 USBProxyService *service = mParent->host()->usbProxyService();
12320 AssertReturn(service, E_FAIL);
12321 return service->autoCaptureDevicesForVM(this);
12322#else
12323 return S_OK;
12324#endif
12325}
12326
12327/**
12328 * Removes all machine filters from the USB proxy service and then calls
12329 * Host::detachAllUSBDevices().
12330 *
12331 * Called by Console from the VM process upon normal VM termination or by
12332 * SessionMachine::uninit() upon abnormal VM termination (from under the
12333 * Machine/SessionMachine lock).
12334 *
12335 * @note Locks what called methods lock.
12336 */
12337STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12338{
12339 LogFlowThisFunc(("\n"));
12340
12341 AutoCaller autoCaller(this);
12342 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12343
12344#ifdef VBOX_WITH_USB
12345 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12346 AssertComRC(rc);
12347 NOREF(rc);
12348
12349 USBProxyService *service = mParent->host()->usbProxyService();
12350 AssertReturn(service, E_FAIL);
12351 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12352#else
12353 NOREF(aDone);
12354 return S_OK;
12355#endif
12356}
12357
12358/**
12359 * @note Locks this object for writing.
12360 */
12361STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12362 IProgress **aProgress)
12363{
12364 LogFlowThisFuncEnter();
12365
12366 AssertReturn(aSession, E_INVALIDARG);
12367 AssertReturn(aProgress, E_INVALIDARG);
12368
12369 AutoCaller autoCaller(this);
12370
12371 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12372 /*
12373 * We don't assert below because it might happen that a non-direct session
12374 * informs us it is closed right after we've been uninitialized -- it's ok.
12375 */
12376 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12377
12378 /* get IInternalSessionControl interface */
12379 ComPtr<IInternalSessionControl> control(aSession);
12380
12381 ComAssertRet(!control.isNull(), E_INVALIDARG);
12382
12383 /* Creating a Progress object requires the VirtualBox lock, and
12384 * thus locking it here is required by the lock order rules. */
12385 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12386
12387 if (control == mData->mSession.mDirectControl)
12388 {
12389 ComAssertRet(aProgress, E_POINTER);
12390
12391 /* The direct session is being normally closed by the client process
12392 * ----------------------------------------------------------------- */
12393
12394 /* go to the closing state (essential for all open*Session() calls and
12395 * for #checkForDeath()) */
12396 Assert(mData->mSession.mState == SessionState_Locked);
12397 mData->mSession.mState = SessionState_Unlocking;
12398
12399 /* set direct control to NULL to release the remote instance */
12400 mData->mSession.mDirectControl.setNull();
12401 LogFlowThisFunc(("Direct control is set to NULL\n"));
12402
12403 if (mData->mSession.mProgress)
12404 {
12405 /* finalize the progress, someone might wait if a frontend
12406 * closes the session before powering on the VM. */
12407 mData->mSession.mProgress->notifyComplete(E_FAIL,
12408 COM_IIDOF(ISession),
12409 getComponentName(),
12410 tr("The VM session was closed before any attempt to power it on"));
12411 mData->mSession.mProgress.setNull();
12412 }
12413
12414 /* Create the progress object the client will use to wait until
12415 * #checkForDeath() is called to uninitialize this session object after
12416 * it releases the IPC semaphore.
12417 * Note! Because we're "reusing" mProgress here, this must be a proxy
12418 * object just like for LaunchVMProcess. */
12419 Assert(mData->mSession.mProgress.isNull());
12420 ComObjPtr<ProgressProxy> progress;
12421 progress.createObject();
12422 ComPtr<IUnknown> pPeer(mPeer);
12423 progress->init(mParent, pPeer,
12424 Bstr(tr("Closing session")).raw(),
12425 FALSE /* aCancelable */);
12426 progress.queryInterfaceTo(aProgress);
12427 mData->mSession.mProgress = progress;
12428 }
12429 else
12430 {
12431 /* the remote session is being normally closed */
12432 Data::Session::RemoteControlList::iterator it =
12433 mData->mSession.mRemoteControls.begin();
12434 while (it != mData->mSession.mRemoteControls.end())
12435 {
12436 if (control == *it)
12437 break;
12438 ++it;
12439 }
12440 BOOL found = it != mData->mSession.mRemoteControls.end();
12441 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12442 E_INVALIDARG);
12443 // This MUST be erase(it), not remove(*it) as the latter triggers a
12444 // very nasty use after free due to the place where the value "lives".
12445 mData->mSession.mRemoteControls.erase(it);
12446 }
12447
12448 /* signal the client watcher thread, because the client is going away */
12449 mParent->updateClientWatcher();
12450
12451 LogFlowThisFuncLeave();
12452 return S_OK;
12453}
12454
12455/**
12456 * @note Locks this object for writing.
12457 */
12458STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12459{
12460 LogFlowThisFuncEnter();
12461
12462 CheckComArgOutPointerValid(aProgress);
12463 CheckComArgOutPointerValid(aStateFilePath);
12464
12465 AutoCaller autoCaller(this);
12466 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12467
12468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12469
12470 AssertReturn( mData->mMachineState == MachineState_Paused
12471 && mConsoleTaskData.mLastState == MachineState_Null
12472 && mConsoleTaskData.strStateFilePath.isEmpty(),
12473 E_FAIL);
12474
12475 /* create a progress object to track operation completion */
12476 ComObjPtr<Progress> pProgress;
12477 pProgress.createObject();
12478 pProgress->init(getVirtualBox(),
12479 static_cast<IMachine *>(this) /* aInitiator */,
12480 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12481 FALSE /* aCancelable */);
12482
12483 Utf8Str strStateFilePath;
12484 /* stateFilePath is null when the machine is not running */
12485 if (mData->mMachineState == MachineState_Paused)
12486 composeSavedStateFilename(strStateFilePath);
12487
12488 /* fill in the console task data */
12489 mConsoleTaskData.mLastState = mData->mMachineState;
12490 mConsoleTaskData.strStateFilePath = strStateFilePath;
12491 mConsoleTaskData.mProgress = pProgress;
12492
12493 /* set the state to Saving (this is expected by Console::SaveState()) */
12494 setMachineState(MachineState_Saving);
12495
12496 strStateFilePath.cloneTo(aStateFilePath);
12497 pProgress.queryInterfaceTo(aProgress);
12498
12499 return S_OK;
12500}
12501
12502/**
12503 * @note Locks mParent + this object for writing.
12504 */
12505STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12506{
12507 LogFlowThisFunc(("\n"));
12508
12509 AutoCaller autoCaller(this);
12510 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12511
12512 /* endSavingState() need mParent lock */
12513 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12514
12515 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12516 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12517 && mConsoleTaskData.mLastState != MachineState_Null
12518 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12519 E_FAIL);
12520
12521 /*
12522 * On failure, set the state to the state we had when BeginSavingState()
12523 * was called (this is expected by Console::SaveState() and the associated
12524 * task). On success the VM process already changed the state to
12525 * MachineState_Saved, so no need to do anything.
12526 */
12527 if (FAILED(iResult))
12528 setMachineState(mConsoleTaskData.mLastState);
12529
12530 return endSavingState(iResult, aErrMsg);
12531}
12532
12533/**
12534 * @note Locks this object for writing.
12535 */
12536STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12537{
12538 LogFlowThisFunc(("\n"));
12539
12540 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12541
12542 AutoCaller autoCaller(this);
12543 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12544
12545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12546
12547 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12548 || mData->mMachineState == MachineState_Teleported
12549 || mData->mMachineState == MachineState_Aborted
12550 , E_FAIL); /** @todo setError. */
12551
12552 Utf8Str stateFilePathFull = aSavedStateFile;
12553 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12554 if (RT_FAILURE(vrc))
12555 return setError(VBOX_E_FILE_ERROR,
12556 tr("Invalid saved state file path '%ls' (%Rrc)"),
12557 aSavedStateFile,
12558 vrc);
12559
12560 mSSData->strStateFilePath = stateFilePathFull;
12561
12562 /* The below setMachineState() will detect the state transition and will
12563 * update the settings file */
12564
12565 return setMachineState(MachineState_Saved);
12566}
12567
12568STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12569 ComSafeArrayOut(BSTR, aValues),
12570 ComSafeArrayOut(LONG64, aTimestamps),
12571 ComSafeArrayOut(BSTR, aFlags))
12572{
12573 LogFlowThisFunc(("\n"));
12574
12575#ifdef VBOX_WITH_GUEST_PROPS
12576 using namespace guestProp;
12577
12578 AutoCaller autoCaller(this);
12579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12580
12581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12582
12583 CheckComArgOutSafeArrayPointerValid(aNames);
12584 CheckComArgOutSafeArrayPointerValid(aValues);
12585 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12586 CheckComArgOutSafeArrayPointerValid(aFlags);
12587
12588 size_t cEntries = mHWData->mGuestProperties.size();
12589 com::SafeArray<BSTR> names(cEntries);
12590 com::SafeArray<BSTR> values(cEntries);
12591 com::SafeArray<LONG64> timestamps(cEntries);
12592 com::SafeArray<BSTR> flags(cEntries);
12593 unsigned i = 0;
12594 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12595 it != mHWData->mGuestProperties.end();
12596 ++it)
12597 {
12598 char szFlags[MAX_FLAGS_LEN + 1];
12599 it->strName.cloneTo(&names[i]);
12600 it->strValue.cloneTo(&values[i]);
12601 timestamps[i] = it->mTimestamp;
12602 /* If it is NULL, keep it NULL. */
12603 if (it->mFlags)
12604 {
12605 writeFlags(it->mFlags, szFlags);
12606 Bstr(szFlags).cloneTo(&flags[i]);
12607 }
12608 else
12609 flags[i] = NULL;
12610 ++i;
12611 }
12612 names.detachTo(ComSafeArrayOutArg(aNames));
12613 values.detachTo(ComSafeArrayOutArg(aValues));
12614 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12615 flags.detachTo(ComSafeArrayOutArg(aFlags));
12616 return S_OK;
12617#else
12618 ReturnComNotImplemented();
12619#endif
12620}
12621
12622STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12623 IN_BSTR aValue,
12624 LONG64 aTimestamp,
12625 IN_BSTR aFlags)
12626{
12627 LogFlowThisFunc(("\n"));
12628
12629#ifdef VBOX_WITH_GUEST_PROPS
12630 using namespace guestProp;
12631
12632 CheckComArgStrNotEmptyOrNull(aName);
12633 CheckComArgNotNull(aValue);
12634 CheckComArgNotNull(aFlags);
12635
12636 try
12637 {
12638 /*
12639 * Convert input up front.
12640 */
12641 Utf8Str utf8Name(aName);
12642 uint32_t fFlags = NILFLAG;
12643 if (aFlags)
12644 {
12645 Utf8Str utf8Flags(aFlags);
12646 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12647 AssertRCReturn(vrc, E_INVALIDARG);
12648 }
12649
12650 /*
12651 * Now grab the object lock, validate the state and do the update.
12652 */
12653 AutoCaller autoCaller(this);
12654 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12655
12656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12657
12658 switch (mData->mMachineState)
12659 {
12660 case MachineState_Paused:
12661 case MachineState_Running:
12662 case MachineState_Teleporting:
12663 case MachineState_TeleportingPausedVM:
12664 case MachineState_LiveSnapshotting:
12665 case MachineState_DeletingSnapshotOnline:
12666 case MachineState_DeletingSnapshotPaused:
12667 case MachineState_Saving:
12668 break;
12669
12670 default:
12671#ifndef DEBUG_sunlover
12672 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12673 VBOX_E_INVALID_VM_STATE);
12674#else
12675 return VBOX_E_INVALID_VM_STATE;
12676#endif
12677 }
12678
12679 setModified(IsModified_MachineData);
12680 mHWData.backup();
12681
12682 /** @todo r=bird: The careful memory handling doesn't work out here because
12683 * the catch block won't undo any damage we've done. So, if push_back throws
12684 * bad_alloc then you've lost the value.
12685 *
12686 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12687 * since values that changes actually bubbles to the end of the list. Using
12688 * something that has an efficient lookup and can tolerate a bit of updates
12689 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12690 * combination of RTStrCache (for sharing names and getting uniqueness into
12691 * the bargain) and hash/tree is another. */
12692 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12693 iter != mHWData->mGuestProperties.end();
12694 ++iter)
12695 if (utf8Name == iter->strName)
12696 {
12697 mHWData->mGuestProperties.erase(iter);
12698 mData->mGuestPropertiesModified = TRUE;
12699 break;
12700 }
12701 if (aValue != NULL)
12702 {
12703 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12704 mHWData->mGuestProperties.push_back(property);
12705 mData->mGuestPropertiesModified = TRUE;
12706 }
12707
12708 /*
12709 * Send a callback notification if appropriate
12710 */
12711 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12712 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12713 RTSTR_MAX,
12714 utf8Name.c_str(),
12715 RTSTR_MAX, NULL)
12716 )
12717 {
12718 alock.release();
12719
12720 mParent->onGuestPropertyChange(mData->mUuid,
12721 aName,
12722 aValue,
12723 aFlags);
12724 }
12725 }
12726 catch (...)
12727 {
12728 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12729 }
12730 return S_OK;
12731#else
12732 ReturnComNotImplemented();
12733#endif
12734}
12735
12736STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12737 IMediumAttachment **aNewAttachment)
12738{
12739 CheckComArgNotNull(aAttachment);
12740 CheckComArgOutPointerValid(aNewAttachment);
12741
12742 AutoCaller autoCaller(this);
12743 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12744
12745 // request the host lock first, since might be calling Host methods for getting host drives;
12746 // next, protect the media tree all the while we're in here, as well as our member variables
12747 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12748 this->lockHandle(),
12749 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12750
12751 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12752
12753 Bstr ctrlName;
12754 LONG lPort;
12755 LONG lDevice;
12756 bool fTempEject;
12757 {
12758 AutoCaller autoAttachCaller(this);
12759 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12760
12761 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12762
12763 /* Need to query the details first, as the IMediumAttachment reference
12764 * might be to the original settings, which we are going to change. */
12765 ctrlName = pAttach->getControllerName();
12766 lPort = pAttach->getPort();
12767 lDevice = pAttach->getDevice();
12768 fTempEject = pAttach->getTempEject();
12769 }
12770
12771 if (!fTempEject)
12772 {
12773 /* Remember previously mounted medium. The medium before taking the
12774 * backup is not necessarily the same thing. */
12775 ComObjPtr<Medium> oldmedium;
12776 oldmedium = pAttach->getMedium();
12777
12778 setModified(IsModified_Storage);
12779 mMediaData.backup();
12780
12781 // The backup operation makes the pAttach reference point to the
12782 // old settings. Re-get the correct reference.
12783 pAttach = findAttachment(mMediaData->mAttachments,
12784 ctrlName.raw(),
12785 lPort,
12786 lDevice);
12787
12788 {
12789 AutoCaller autoAttachCaller(this);
12790 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12791
12792 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12793 if (!oldmedium.isNull())
12794 oldmedium->removeBackReference(mData->mUuid);
12795
12796 pAttach->updateMedium(NULL);
12797 pAttach->updateEjected();
12798 }
12799
12800 setModified(IsModified_Storage);
12801 }
12802 else
12803 {
12804 {
12805 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12806 pAttach->updateEjected();
12807 }
12808 }
12809
12810 pAttach.queryInterfaceTo(aNewAttachment);
12811
12812 return S_OK;
12813}
12814
12815// public methods only for internal purposes
12816/////////////////////////////////////////////////////////////////////////////
12817
12818/**
12819 * Called from the client watcher thread to check for expected or unexpected
12820 * death of the client process that has a direct session to this machine.
12821 *
12822 * On Win32 and on OS/2, this method is called only when we've got the
12823 * mutex (i.e. the client has either died or terminated normally) so it always
12824 * returns @c true (the client is terminated, the session machine is
12825 * uninitialized).
12826 *
12827 * On other platforms, the method returns @c true if the client process has
12828 * terminated normally or abnormally and the session machine was uninitialized,
12829 * and @c false if the client process is still alive.
12830 *
12831 * @note Locks this object for writing.
12832 */
12833bool SessionMachine::checkForDeath()
12834{
12835 Uninit::Reason reason;
12836 bool terminated = false;
12837
12838 /* Enclose autoCaller with a block because calling uninit() from under it
12839 * will deadlock. */
12840 {
12841 AutoCaller autoCaller(this);
12842 if (!autoCaller.isOk())
12843 {
12844 /* return true if not ready, to cause the client watcher to exclude
12845 * the corresponding session from watching */
12846 LogFlowThisFunc(("Already uninitialized!\n"));
12847 return true;
12848 }
12849
12850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12851
12852 /* Determine the reason of death: if the session state is Closing here,
12853 * everything is fine. Otherwise it means that the client did not call
12854 * OnSessionEnd() before it released the IPC semaphore. This may happen
12855 * either because the client process has abnormally terminated, or
12856 * because it simply forgot to call ISession::Close() before exiting. We
12857 * threat the latter also as an abnormal termination (see
12858 * Session::uninit() for details). */
12859 reason = mData->mSession.mState == SessionState_Unlocking ?
12860 Uninit::Normal :
12861 Uninit::Abnormal;
12862
12863#if defined(RT_OS_WINDOWS)
12864
12865 AssertMsg(mIPCSem, ("semaphore must be created"));
12866
12867 /* release the IPC mutex */
12868 ::ReleaseMutex(mIPCSem);
12869
12870 terminated = true;
12871
12872#elif defined(RT_OS_OS2)
12873
12874 AssertMsg(mIPCSem, ("semaphore must be created"));
12875
12876 /* release the IPC mutex */
12877 ::DosReleaseMutexSem(mIPCSem);
12878
12879 terminated = true;
12880
12881#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12882
12883 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
12884
12885 int val = ::semctl(mIPCSem, 0, GETVAL);
12886 if (val > 0)
12887 {
12888 /* the semaphore is signaled, meaning the session is terminated */
12889 terminated = true;
12890 }
12891
12892#else
12893# error "Port me!"
12894#endif
12895
12896 } /* AutoCaller block */
12897
12898 if (terminated)
12899 uninit(reason);
12900
12901 return terminated;
12902}
12903
12904/**
12905 * @note Locks this object for reading.
12906 */
12907HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
12908{
12909 LogFlowThisFunc(("\n"));
12910
12911 AutoCaller autoCaller(this);
12912 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12913
12914 ComPtr<IInternalSessionControl> directControl;
12915 {
12916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12917 directControl = mData->mSession.mDirectControl;
12918 }
12919
12920 /* ignore notifications sent after #OnSessionEnd() is called */
12921 if (!directControl)
12922 return S_OK;
12923
12924 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
12925}
12926
12927/**
12928 * @note Locks this object for reading.
12929 */
12930HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
12931 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
12932{
12933 LogFlowThisFunc(("\n"));
12934
12935 AutoCaller autoCaller(this);
12936 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12937
12938 ComPtr<IInternalSessionControl> directControl;
12939 {
12940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12941 directControl = mData->mSession.mDirectControl;
12942 }
12943
12944 /* ignore notifications sent after #OnSessionEnd() is called */
12945 if (!directControl)
12946 return S_OK;
12947 /*
12948 * instead acting like callback we ask IVirtualBox deliver corresponding event
12949 */
12950
12951 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
12952 return S_OK;
12953}
12954
12955/**
12956 * @note Locks this object for reading.
12957 */
12958HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
12959{
12960 LogFlowThisFunc(("\n"));
12961
12962 AutoCaller autoCaller(this);
12963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12964
12965 ComPtr<IInternalSessionControl> directControl;
12966 {
12967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12968 directControl = mData->mSession.mDirectControl;
12969 }
12970
12971 /* ignore notifications sent after #OnSessionEnd() is called */
12972 if (!directControl)
12973 return S_OK;
12974
12975 return directControl->OnSerialPortChange(serialPort);
12976}
12977
12978/**
12979 * @note Locks this object for reading.
12980 */
12981HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
12982{
12983 LogFlowThisFunc(("\n"));
12984
12985 AutoCaller autoCaller(this);
12986 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12987
12988 ComPtr<IInternalSessionControl> directControl;
12989 {
12990 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12991 directControl = mData->mSession.mDirectControl;
12992 }
12993
12994 /* ignore notifications sent after #OnSessionEnd() is called */
12995 if (!directControl)
12996 return S_OK;
12997
12998 return directControl->OnParallelPortChange(parallelPort);
12999}
13000
13001/**
13002 * @note Locks this object for reading.
13003 */
13004HRESULT SessionMachine::onStorageControllerChange()
13005{
13006 LogFlowThisFunc(("\n"));
13007
13008 AutoCaller autoCaller(this);
13009 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13010
13011 ComPtr<IInternalSessionControl> directControl;
13012 {
13013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13014 directControl = mData->mSession.mDirectControl;
13015 }
13016
13017 /* ignore notifications sent after #OnSessionEnd() is called */
13018 if (!directControl)
13019 return S_OK;
13020
13021 return directControl->OnStorageControllerChange();
13022}
13023
13024/**
13025 * @note Locks this object for reading.
13026 */
13027HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13028{
13029 LogFlowThisFunc(("\n"));
13030
13031 AutoCaller autoCaller(this);
13032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13033
13034 ComPtr<IInternalSessionControl> directControl;
13035 {
13036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13037 directControl = mData->mSession.mDirectControl;
13038 }
13039
13040 /* ignore notifications sent after #OnSessionEnd() is called */
13041 if (!directControl)
13042 return S_OK;
13043
13044 return directControl->OnMediumChange(aAttachment, aForce);
13045}
13046
13047/**
13048 * @note Locks this object for reading.
13049 */
13050HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13051{
13052 LogFlowThisFunc(("\n"));
13053
13054 AutoCaller autoCaller(this);
13055 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13056
13057 ComPtr<IInternalSessionControl> directControl;
13058 {
13059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13060 directControl = mData->mSession.mDirectControl;
13061 }
13062
13063 /* ignore notifications sent after #OnSessionEnd() is called */
13064 if (!directControl)
13065 return S_OK;
13066
13067 return directControl->OnCPUChange(aCPU, aRemove);
13068}
13069
13070HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13071{
13072 LogFlowThisFunc(("\n"));
13073
13074 AutoCaller autoCaller(this);
13075 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13076
13077 ComPtr<IInternalSessionControl> directControl;
13078 {
13079 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13080 directControl = mData->mSession.mDirectControl;
13081 }
13082
13083 /* ignore notifications sent after #OnSessionEnd() is called */
13084 if (!directControl)
13085 return S_OK;
13086
13087 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13088}
13089
13090/**
13091 * @note Locks this object for reading.
13092 */
13093HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13094{
13095 LogFlowThisFunc(("\n"));
13096
13097 AutoCaller autoCaller(this);
13098 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13099
13100 ComPtr<IInternalSessionControl> directControl;
13101 {
13102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13103 directControl = mData->mSession.mDirectControl;
13104 }
13105
13106 /* ignore notifications sent after #OnSessionEnd() is called */
13107 if (!directControl)
13108 return S_OK;
13109
13110 return directControl->OnVRDEServerChange(aRestart);
13111}
13112
13113/**
13114 * @note Locks this object for reading.
13115 */
13116HRESULT SessionMachine::onUSBControllerChange()
13117{
13118 LogFlowThisFunc(("\n"));
13119
13120 AutoCaller autoCaller(this);
13121 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13122
13123 ComPtr<IInternalSessionControl> directControl;
13124 {
13125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13126 directControl = mData->mSession.mDirectControl;
13127 }
13128
13129 /* ignore notifications sent after #OnSessionEnd() is called */
13130 if (!directControl)
13131 return S_OK;
13132
13133 return directControl->OnUSBControllerChange();
13134}
13135
13136/**
13137 * @note Locks this object for reading.
13138 */
13139HRESULT SessionMachine::onSharedFolderChange()
13140{
13141 LogFlowThisFunc(("\n"));
13142
13143 AutoCaller autoCaller(this);
13144 AssertComRCReturnRC(autoCaller.rc());
13145
13146 ComPtr<IInternalSessionControl> directControl;
13147 {
13148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13149 directControl = mData->mSession.mDirectControl;
13150 }
13151
13152 /* ignore notifications sent after #OnSessionEnd() is called */
13153 if (!directControl)
13154 return S_OK;
13155
13156 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13157}
13158
13159/**
13160 * @note Locks this object for reading.
13161 */
13162HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13163{
13164 LogFlowThisFunc(("\n"));
13165
13166 AutoCaller autoCaller(this);
13167 AssertComRCReturnRC(autoCaller.rc());
13168
13169 ComPtr<IInternalSessionControl> directControl;
13170 {
13171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13172 directControl = mData->mSession.mDirectControl;
13173 }
13174
13175 /* ignore notifications sent after #OnSessionEnd() is called */
13176 if (!directControl)
13177 return S_OK;
13178
13179 return directControl->OnClipboardModeChange(aClipboardMode);
13180}
13181
13182/**
13183 * @note Locks this object for reading.
13184 */
13185HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13186{
13187 LogFlowThisFunc(("\n"));
13188
13189 AutoCaller autoCaller(this);
13190 AssertComRCReturnRC(autoCaller.rc());
13191
13192 ComPtr<IInternalSessionControl> directControl;
13193 {
13194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13195 directControl = mData->mSession.mDirectControl;
13196 }
13197
13198 /* ignore notifications sent after #OnSessionEnd() is called */
13199 if (!directControl)
13200 return S_OK;
13201
13202 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13203}
13204
13205/**
13206 * @note Locks this object for reading.
13207 */
13208HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13209{
13210 LogFlowThisFunc(("\n"));
13211
13212 AutoCaller autoCaller(this);
13213 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13214
13215 ComPtr<IInternalSessionControl> directControl;
13216 {
13217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13218 directControl = mData->mSession.mDirectControl;
13219 }
13220
13221 /* ignore notifications sent after #OnSessionEnd() is called */
13222 if (!directControl)
13223 return S_OK;
13224
13225 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13226}
13227
13228/**
13229 * @note Locks this object for reading.
13230 */
13231HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13232{
13233 LogFlowThisFunc(("\n"));
13234
13235 AutoCaller autoCaller(this);
13236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13237
13238 ComPtr<IInternalSessionControl> directControl;
13239 {
13240 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13241 directControl = mData->mSession.mDirectControl;
13242 }
13243
13244 /* ignore notifications sent after #OnSessionEnd() is called */
13245 if (!directControl)
13246 return S_OK;
13247
13248 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13249}
13250
13251/**
13252 * Returns @c true if this machine's USB controller reports it has a matching
13253 * filter for the given USB device and @c false otherwise.
13254 *
13255 * @note locks this object for reading.
13256 */
13257bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13258{
13259 AutoCaller autoCaller(this);
13260 /* silently return if not ready -- this method may be called after the
13261 * direct machine session has been called */
13262 if (!autoCaller.isOk())
13263 return false;
13264
13265#ifdef VBOX_WITH_USB
13266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13267
13268 switch (mData->mMachineState)
13269 {
13270 case MachineState_Starting:
13271 case MachineState_Restoring:
13272 case MachineState_TeleportingIn:
13273 case MachineState_Paused:
13274 case MachineState_Running:
13275 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13276 * elsewhere... */
13277 alock.release();
13278 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13279 default: break;
13280 }
13281#else
13282 NOREF(aDevice);
13283 NOREF(aMaskedIfs);
13284#endif
13285 return false;
13286}
13287
13288/**
13289 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13290 */
13291HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13292 IVirtualBoxErrorInfo *aError,
13293 ULONG aMaskedIfs)
13294{
13295 LogFlowThisFunc(("\n"));
13296
13297 AutoCaller autoCaller(this);
13298
13299 /* This notification may happen after the machine object has been
13300 * uninitialized (the session was closed), so don't assert. */
13301 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13302
13303 ComPtr<IInternalSessionControl> directControl;
13304 {
13305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13306 directControl = mData->mSession.mDirectControl;
13307 }
13308
13309 /* fail on notifications sent after #OnSessionEnd() is called, it is
13310 * expected by the caller */
13311 if (!directControl)
13312 return E_FAIL;
13313
13314 /* No locks should be held at this point. */
13315 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13316 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13317
13318 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13319}
13320
13321/**
13322 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13323 */
13324HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13325 IVirtualBoxErrorInfo *aError)
13326{
13327 LogFlowThisFunc(("\n"));
13328
13329 AutoCaller autoCaller(this);
13330
13331 /* This notification may happen after the machine object has been
13332 * uninitialized (the session was closed), so don't assert. */
13333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13334
13335 ComPtr<IInternalSessionControl> directControl;
13336 {
13337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13338 directControl = mData->mSession.mDirectControl;
13339 }
13340
13341 /* fail on notifications sent after #OnSessionEnd() is called, it is
13342 * expected by the caller */
13343 if (!directControl)
13344 return E_FAIL;
13345
13346 /* No locks should be held at this point. */
13347 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13348 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13349
13350 return directControl->OnUSBDeviceDetach(aId, aError);
13351}
13352
13353// protected methods
13354/////////////////////////////////////////////////////////////////////////////
13355
13356/**
13357 * Helper method to finalize saving the state.
13358 *
13359 * @note Must be called from under this object's lock.
13360 *
13361 * @param aRc S_OK if the snapshot has been taken successfully
13362 * @param aErrMsg human readable error message for failure
13363 *
13364 * @note Locks mParent + this objects for writing.
13365 */
13366HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13367{
13368 LogFlowThisFuncEnter();
13369
13370 AutoCaller autoCaller(this);
13371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13372
13373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13374
13375 HRESULT rc = S_OK;
13376
13377 if (SUCCEEDED(aRc))
13378 {
13379 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13380
13381 /* save all VM settings */
13382 rc = saveSettings(NULL);
13383 // no need to check whether VirtualBox.xml needs saving also since
13384 // we can't have a name change pending at this point
13385 }
13386 else
13387 {
13388 // delete the saved state file (it might have been already created);
13389 // we need not check whether this is shared with a snapshot here because
13390 // we certainly created this saved state file here anew
13391 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13392 }
13393
13394 /* notify the progress object about operation completion */
13395 Assert(mConsoleTaskData.mProgress);
13396 if (SUCCEEDED(aRc))
13397 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13398 else
13399 {
13400 if (aErrMsg.length())
13401 mConsoleTaskData.mProgress->notifyComplete(aRc,
13402 COM_IIDOF(ISession),
13403 getComponentName(),
13404 aErrMsg.c_str());
13405 else
13406 mConsoleTaskData.mProgress->notifyComplete(aRc);
13407 }
13408
13409 /* clear out the temporary saved state data */
13410 mConsoleTaskData.mLastState = MachineState_Null;
13411 mConsoleTaskData.strStateFilePath.setNull();
13412 mConsoleTaskData.mProgress.setNull();
13413
13414 LogFlowThisFuncLeave();
13415 return rc;
13416}
13417
13418/**
13419 * Deletes the given file if it is no longer in use by either the current machine state
13420 * (if the machine is "saved") or any of the machine's snapshots.
13421 *
13422 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13423 * but is different for each SnapshotMachine. When calling this, the order of calling this
13424 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13425 * is therefore critical. I know, it's all rather messy.
13426 *
13427 * @param strStateFile
13428 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13429 */
13430void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13431 Snapshot *pSnapshotToIgnore)
13432{
13433 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13434 if ( (strStateFile.isNotEmpty())
13435 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13436 )
13437 // ... and it must also not be shared with other snapshots
13438 if ( !mData->mFirstSnapshot
13439 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13440 // this checks the SnapshotMachine's state file paths
13441 )
13442 RTFileDelete(strStateFile.c_str());
13443}
13444
13445/**
13446 * Locks the attached media.
13447 *
13448 * All attached hard disks are locked for writing and DVD/floppy are locked for
13449 * reading. Parents of attached hard disks (if any) are locked for reading.
13450 *
13451 * This method also performs accessibility check of all media it locks: if some
13452 * media is inaccessible, the method will return a failure and a bunch of
13453 * extended error info objects per each inaccessible medium.
13454 *
13455 * Note that this method is atomic: if it returns a success, all media are
13456 * locked as described above; on failure no media is locked at all (all
13457 * succeeded individual locks will be undone).
13458 *
13459 * This method is intended to be called when the machine is in Starting or
13460 * Restoring state and asserts otherwise.
13461 *
13462 * The locks made by this method must be undone by calling #unlockMedia() when
13463 * no more needed.
13464 */
13465HRESULT SessionMachine::lockMedia()
13466{
13467 AutoCaller autoCaller(this);
13468 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13469
13470 AutoMultiWriteLock2 alock(this->lockHandle(),
13471 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13472
13473 AssertReturn( mData->mMachineState == MachineState_Starting
13474 || mData->mMachineState == MachineState_Restoring
13475 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13476 /* bail out if trying to lock things with already set up locking */
13477 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13478
13479 clearError();
13480 MultiResult mrc(S_OK);
13481
13482 /* Collect locking information for all medium objects attached to the VM. */
13483 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13484 it != mMediaData->mAttachments.end();
13485 ++it)
13486 {
13487 MediumAttachment* pAtt = *it;
13488 DeviceType_T devType = pAtt->getType();
13489 Medium *pMedium = pAtt->getMedium();
13490
13491 MediumLockList *pMediumLockList(new MediumLockList());
13492 // There can be attachments without a medium (floppy/dvd), and thus
13493 // it's impossible to create a medium lock list. It still makes sense
13494 // to have the empty medium lock list in the map in case a medium is
13495 // attached later.
13496 if (pMedium != NULL)
13497 {
13498 MediumType_T mediumType = pMedium->getType();
13499 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13500 || mediumType == MediumType_Shareable;
13501 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13502
13503 alock.release();
13504 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13505 !fIsReadOnlyLock /* fMediumLockWrite */,
13506 NULL,
13507 *pMediumLockList);
13508 alock.acquire();
13509 if (FAILED(mrc))
13510 {
13511 delete pMediumLockList;
13512 mData->mSession.mLockedMedia.Clear();
13513 break;
13514 }
13515 }
13516
13517 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13518 if (FAILED(rc))
13519 {
13520 mData->mSession.mLockedMedia.Clear();
13521 mrc = setError(rc,
13522 tr("Collecting locking information for all attached media failed"));
13523 break;
13524 }
13525 }
13526
13527 if (SUCCEEDED(mrc))
13528 {
13529 /* Now lock all media. If this fails, nothing is locked. */
13530 alock.release();
13531 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13532 alock.acquire();
13533 if (FAILED(rc))
13534 {
13535 mrc = setError(rc,
13536 tr("Locking of attached media failed"));
13537 }
13538 }
13539
13540 return mrc;
13541}
13542
13543/**
13544 * Undoes the locks made by by #lockMedia().
13545 */
13546void SessionMachine::unlockMedia()
13547{
13548 AutoCaller autoCaller(this);
13549 AssertComRCReturnVoid(autoCaller.rc());
13550
13551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13552
13553 /* we may be holding important error info on the current thread;
13554 * preserve it */
13555 ErrorInfoKeeper eik;
13556
13557 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13558 AssertComRC(rc);
13559}
13560
13561/**
13562 * Helper to change the machine state (reimplementation).
13563 *
13564 * @note Locks this object for writing.
13565 * @note This method must not call saveSettings or SaveSettings, otherwise
13566 * it can cause crashes in random places due to unexpectedly committing
13567 * the current settings. The caller is responsible for that. The call
13568 * to saveStateSettings is fine, because this method does not commit.
13569 */
13570HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13571{
13572 LogFlowThisFuncEnter();
13573 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13574
13575 AutoCaller autoCaller(this);
13576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13577
13578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13579
13580 MachineState_T oldMachineState = mData->mMachineState;
13581
13582 AssertMsgReturn(oldMachineState != aMachineState,
13583 ("oldMachineState=%s, aMachineState=%s\n",
13584 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13585 E_FAIL);
13586
13587 HRESULT rc = S_OK;
13588
13589 int stsFlags = 0;
13590 bool deleteSavedState = false;
13591
13592 /* detect some state transitions */
13593
13594 if ( ( oldMachineState == MachineState_Saved
13595 && aMachineState == MachineState_Restoring)
13596 || ( ( oldMachineState == MachineState_PoweredOff
13597 || oldMachineState == MachineState_Teleported
13598 || oldMachineState == MachineState_Aborted
13599 )
13600 && ( aMachineState == MachineState_TeleportingIn
13601 || aMachineState == MachineState_Starting
13602 )
13603 )
13604 )
13605 {
13606 /* The EMT thread is about to start */
13607
13608 /* Nothing to do here for now... */
13609
13610 /// @todo NEWMEDIA don't let mDVDDrive and other children
13611 /// change anything when in the Starting/Restoring state
13612 }
13613 else if ( ( oldMachineState == MachineState_Running
13614 || oldMachineState == MachineState_Paused
13615 || oldMachineState == MachineState_Teleporting
13616 || oldMachineState == MachineState_LiveSnapshotting
13617 || oldMachineState == MachineState_Stuck
13618 || oldMachineState == MachineState_Starting
13619 || oldMachineState == MachineState_Stopping
13620 || oldMachineState == MachineState_Saving
13621 || oldMachineState == MachineState_Restoring
13622 || oldMachineState == MachineState_TeleportingPausedVM
13623 || oldMachineState == MachineState_TeleportingIn
13624 )
13625 && ( aMachineState == MachineState_PoweredOff
13626 || aMachineState == MachineState_Saved
13627 || aMachineState == MachineState_Teleported
13628 || aMachineState == MachineState_Aborted
13629 )
13630 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13631 * snapshot */
13632 && ( mConsoleTaskData.mSnapshot.isNull()
13633 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13634 )
13635 )
13636 {
13637 /* The EMT thread has just stopped, unlock attached media. Note that as
13638 * opposed to locking that is done from Console, we do unlocking here
13639 * because the VM process may have aborted before having a chance to
13640 * properly unlock all media it locked. */
13641
13642 unlockMedia();
13643 }
13644
13645 if (oldMachineState == MachineState_Restoring)
13646 {
13647 if (aMachineState != MachineState_Saved)
13648 {
13649 /*
13650 * delete the saved state file once the machine has finished
13651 * restoring from it (note that Console sets the state from
13652 * Restoring to Saved if the VM couldn't restore successfully,
13653 * to give the user an ability to fix an error and retry --
13654 * we keep the saved state file in this case)
13655 */
13656 deleteSavedState = true;
13657 }
13658 }
13659 else if ( oldMachineState == MachineState_Saved
13660 && ( aMachineState == MachineState_PoweredOff
13661 || aMachineState == MachineState_Aborted
13662 || aMachineState == MachineState_Teleported
13663 )
13664 )
13665 {
13666 /*
13667 * delete the saved state after Console::ForgetSavedState() is called
13668 * or if the VM process (owning a direct VM session) crashed while the
13669 * VM was Saved
13670 */
13671
13672 /// @todo (dmik)
13673 // Not sure that deleting the saved state file just because of the
13674 // client death before it attempted to restore the VM is a good
13675 // thing. But when it crashes we need to go to the Aborted state
13676 // which cannot have the saved state file associated... The only
13677 // way to fix this is to make the Aborted condition not a VM state
13678 // but a bool flag: i.e., when a crash occurs, set it to true and
13679 // change the state to PoweredOff or Saved depending on the
13680 // saved state presence.
13681
13682 deleteSavedState = true;
13683 mData->mCurrentStateModified = TRUE;
13684 stsFlags |= SaveSTS_CurStateModified;
13685 }
13686
13687 if ( aMachineState == MachineState_Starting
13688 || aMachineState == MachineState_Restoring
13689 || aMachineState == MachineState_TeleportingIn
13690 )
13691 {
13692 /* set the current state modified flag to indicate that the current
13693 * state is no more identical to the state in the
13694 * current snapshot */
13695 if (!mData->mCurrentSnapshot.isNull())
13696 {
13697 mData->mCurrentStateModified = TRUE;
13698 stsFlags |= SaveSTS_CurStateModified;
13699 }
13700 }
13701
13702 if (deleteSavedState)
13703 {
13704 if (mRemoveSavedState)
13705 {
13706 Assert(!mSSData->strStateFilePath.isEmpty());
13707
13708 // it is safe to delete the saved state file if ...
13709 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13710 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13711 // ... none of the snapshots share the saved state file
13712 )
13713 RTFileDelete(mSSData->strStateFilePath.c_str());
13714 }
13715
13716 mSSData->strStateFilePath.setNull();
13717 stsFlags |= SaveSTS_StateFilePath;
13718 }
13719
13720 /* redirect to the underlying peer machine */
13721 mPeer->setMachineState(aMachineState);
13722
13723 if ( aMachineState == MachineState_PoweredOff
13724 || aMachineState == MachineState_Teleported
13725 || aMachineState == MachineState_Aborted
13726 || aMachineState == MachineState_Saved)
13727 {
13728 /* the machine has stopped execution
13729 * (or the saved state file was adopted) */
13730 stsFlags |= SaveSTS_StateTimeStamp;
13731 }
13732
13733 if ( ( oldMachineState == MachineState_PoweredOff
13734 || oldMachineState == MachineState_Aborted
13735 || oldMachineState == MachineState_Teleported
13736 )
13737 && aMachineState == MachineState_Saved)
13738 {
13739 /* the saved state file was adopted */
13740 Assert(!mSSData->strStateFilePath.isEmpty());
13741 stsFlags |= SaveSTS_StateFilePath;
13742 }
13743
13744#ifdef VBOX_WITH_GUEST_PROPS
13745 if ( aMachineState == MachineState_PoweredOff
13746 || aMachineState == MachineState_Aborted
13747 || aMachineState == MachineState_Teleported)
13748 {
13749 /* Make sure any transient guest properties get removed from the
13750 * property store on shutdown. */
13751
13752 HWData::GuestPropertyList::iterator it;
13753 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13754 if (!fNeedsSaving)
13755 for (it = mHWData->mGuestProperties.begin();
13756 it != mHWData->mGuestProperties.end(); ++it)
13757 if ( (it->mFlags & guestProp::TRANSIENT)
13758 || (it->mFlags & guestProp::TRANSRESET))
13759 {
13760 fNeedsSaving = true;
13761 break;
13762 }
13763 if (fNeedsSaving)
13764 {
13765 mData->mCurrentStateModified = TRUE;
13766 stsFlags |= SaveSTS_CurStateModified;
13767 }
13768 }
13769#endif
13770
13771 rc = saveStateSettings(stsFlags);
13772
13773 if ( ( oldMachineState != MachineState_PoweredOff
13774 && oldMachineState != MachineState_Aborted
13775 && oldMachineState != MachineState_Teleported
13776 )
13777 && ( aMachineState == MachineState_PoweredOff
13778 || aMachineState == MachineState_Aborted
13779 || aMachineState == MachineState_Teleported
13780 )
13781 )
13782 {
13783 /* we've been shut down for any reason */
13784 /* no special action so far */
13785 }
13786
13787 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13788 LogFlowThisFuncLeave();
13789 return rc;
13790}
13791
13792/**
13793 * Sends the current machine state value to the VM process.
13794 *
13795 * @note Locks this object for reading, then calls a client process.
13796 */
13797HRESULT SessionMachine::updateMachineStateOnClient()
13798{
13799 AutoCaller autoCaller(this);
13800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13801
13802 ComPtr<IInternalSessionControl> directControl;
13803 {
13804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13805 AssertReturn(!!mData, E_FAIL);
13806 directControl = mData->mSession.mDirectControl;
13807
13808 /* directControl may be already set to NULL here in #OnSessionEnd()
13809 * called too early by the direct session process while there is still
13810 * some operation (like deleting the snapshot) in progress. The client
13811 * process in this case is waiting inside Session::close() for the
13812 * "end session" process object to complete, while #uninit() called by
13813 * #checkForDeath() on the Watcher thread is waiting for the pending
13814 * operation to complete. For now, we accept this inconsistent behavior
13815 * and simply do nothing here. */
13816
13817 if (mData->mSession.mState == SessionState_Unlocking)
13818 return S_OK;
13819
13820 AssertReturn(!directControl.isNull(), E_FAIL);
13821 }
13822
13823 return directControl->UpdateMachineState(mData->mMachineState);
13824}
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