VirtualBox

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

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

Main: Increased guest property lookup performance by using a map for internal data, leaving lock before posting change event.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 468.7 KB
Line 
1/* $Id: MachineImpl.cpp 44167 2012-12-19 16:40:41Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#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 = false;
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.isValid(), 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
857 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
858 mParent->unregisterMachineMedia(uuidMachine);
859
860 // has machine been modified?
861 if (mData->flModifications)
862 {
863 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
864 rollback(false /* aNotify */);
865 }
866
867 if (mData->mAccessible)
868 uninitDataAndChildObjects();
869
870 /* free the essential data structure last */
871 mData.free();
872
873 LogFlowThisFuncLeave();
874}
875
876// IMachine properties
877/////////////////////////////////////////////////////////////////////////////
878
879STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
880{
881 CheckComArgOutPointerValid(aParent);
882
883 AutoLimitedCaller autoCaller(this);
884 if (FAILED(autoCaller.rc())) return autoCaller.rc();
885
886 /* mParent is constant during life time, no need to lock */
887 ComObjPtr<VirtualBox> pVirtualBox(mParent);
888 pVirtualBox.queryInterfaceTo(aParent);
889
890 return S_OK;
891}
892
893STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
894{
895 CheckComArgOutPointerValid(aAccessible);
896
897 AutoLimitedCaller autoCaller(this);
898 if (FAILED(autoCaller.rc())) return autoCaller.rc();
899
900 LogFlowThisFunc(("ENTER\n"));
901
902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
903
904 HRESULT rc = S_OK;
905
906 if (!mData->mAccessible)
907 {
908 /* try to initialize the VM once more if not accessible */
909
910 AutoReinitSpan autoReinitSpan(this);
911 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
912
913#ifdef DEBUG
914 LogFlowThisFunc(("Dumping media backreferences\n"));
915 mParent->dumpAllBackRefs();
916#endif
917
918 if (mData->pMachineConfigFile)
919 {
920 // reset the XML file to force loadSettings() (called from registeredInit())
921 // to parse it again; the file might have changed
922 delete mData->pMachineConfigFile;
923 mData->pMachineConfigFile = NULL;
924 }
925
926 rc = registeredInit();
927
928 if (SUCCEEDED(rc) && mData->mAccessible)
929 {
930 autoReinitSpan.setSucceeded();
931
932 /* make sure interesting parties will notice the accessibility
933 * state change */
934 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
935 mParent->onMachineDataChange(mData->mUuid);
936 }
937 }
938
939 if (SUCCEEDED(rc))
940 *aAccessible = mData->mAccessible;
941
942 LogFlowThisFuncLeave();
943
944 return rc;
945}
946
947STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
948{
949 CheckComArgOutPointerValid(aAccessError);
950
951 AutoLimitedCaller autoCaller(this);
952 if (FAILED(autoCaller.rc())) return autoCaller.rc();
953
954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
955
956 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
957 {
958 /* return shortly */
959 aAccessError = NULL;
960 return S_OK;
961 }
962
963 HRESULT rc = S_OK;
964
965 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
966 rc = errorInfo.createObject();
967 if (SUCCEEDED(rc))
968 {
969 errorInfo->init(mData->mAccessError.getResultCode(),
970 mData->mAccessError.getInterfaceID().ref(),
971 Utf8Str(mData->mAccessError.getComponent()).c_str(),
972 Utf8Str(mData->mAccessError.getText()));
973 rc = errorInfo.queryInterfaceTo(aAccessError);
974 }
975
976 return rc;
977}
978
979STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
980{
981 CheckComArgOutPointerValid(aName);
982
983 AutoCaller autoCaller(this);
984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
985
986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
987
988 mUserData->s.strName.cloneTo(aName);
989
990 return S_OK;
991}
992
993STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
994{
995 CheckComArgStrNotEmptyOrNull(aName);
996
997 AutoCaller autoCaller(this);
998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
999
1000 // prohibit setting a UUID only as the machine name, or else it can
1001 // never be found by findMachine()
1002 Guid test(aName);
1003
1004 if (test.isValid())
1005 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1006
1007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1008
1009 HRESULT rc = checkStateDependency(MutableStateDep);
1010 if (FAILED(rc)) return rc;
1011
1012 setModified(IsModified_MachineData);
1013 mUserData.backup();
1014 mUserData->s.strName = aName;
1015
1016 return S_OK;
1017}
1018
1019STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1020{
1021 CheckComArgOutPointerValid(aDescription);
1022
1023 AutoCaller autoCaller(this);
1024 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1025
1026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
1028 mUserData->s.strDescription.cloneTo(aDescription);
1029
1030 return S_OK;
1031}
1032
1033STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1034{
1035 AutoCaller autoCaller(this);
1036 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1037
1038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 // this can be done in principle in any state as it doesn't affect the VM
1041 // significantly, but play safe by not messing around while complex
1042 // activities are going on
1043 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1044 if (FAILED(rc)) return rc;
1045
1046 setModified(IsModified_MachineData);
1047 mUserData.backup();
1048 mUserData->s.strDescription = aDescription;
1049
1050 return S_OK;
1051}
1052
1053STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1054{
1055 CheckComArgOutPointerValid(aId);
1056
1057 AutoLimitedCaller autoCaller(this);
1058 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1059
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 mData->mUuid.toUtf16().cloneTo(aId);
1063
1064 return S_OK;
1065}
1066
1067STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1068{
1069 CheckComArgOutSafeArrayPointerValid(aGroups);
1070
1071 AutoCaller autoCaller(this);
1072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1073
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1076 size_t i = 0;
1077 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1078 it != mUserData->s.llGroups.end();
1079 ++it, i++)
1080 {
1081 Bstr tmp = *it;
1082 tmp.cloneTo(&groups[i]);
1083 }
1084 groups.detachTo(ComSafeArrayOutArg(aGroups));
1085
1086 return S_OK;
1087}
1088
1089STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1090{
1091 AutoCaller autoCaller(this);
1092 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1093
1094 StringsList llGroups;
1095 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1096 if (FAILED(rc))
1097 return rc;
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 // changing machine groups is possible while the VM is offline
1102 rc = checkStateDependency(OfflineStateDep);
1103 if (FAILED(rc)) return rc;
1104
1105 setModified(IsModified_MachineData);
1106 mUserData.backup();
1107 mUserData->s.llGroups = llGroups;
1108
1109 return S_OK;
1110}
1111
1112STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1113{
1114 CheckComArgOutPointerValid(aOSTypeId);
1115
1116 AutoCaller autoCaller(this);
1117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1118
1119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 mUserData->s.strOsType.cloneTo(aOSTypeId);
1122
1123 return S_OK;
1124}
1125
1126STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1127{
1128 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1129
1130 AutoCaller autoCaller(this);
1131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1132
1133 /* look up the object by Id to check it is valid */
1134 ComPtr<IGuestOSType> guestOSType;
1135 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1136 if (FAILED(rc)) return rc;
1137
1138 /* when setting, always use the "etalon" value for consistency -- lookup
1139 * by ID is case-insensitive and the input value may have different case */
1140 Bstr osTypeId;
1141 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1142 if (FAILED(rc)) return rc;
1143
1144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 rc = checkStateDependency(MutableStateDep);
1147 if (FAILED(rc)) return rc;
1148
1149 setModified(IsModified_MachineData);
1150 mUserData.backup();
1151 mUserData->s.strOsType = osTypeId;
1152
1153 return S_OK;
1154}
1155
1156
1157STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1158{
1159 CheckComArgOutPointerValid(aFirmwareType);
1160
1161 AutoCaller autoCaller(this);
1162 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1163
1164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1165
1166 *aFirmwareType = mHWData->mFirmwareType;
1167
1168 return S_OK;
1169}
1170
1171STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1172{
1173 AutoCaller autoCaller(this);
1174 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1176
1177 HRESULT rc = checkStateDependency(MutableStateDep);
1178 if (FAILED(rc)) return rc;
1179
1180 setModified(IsModified_MachineData);
1181 mHWData.backup();
1182 mHWData->mFirmwareType = aFirmwareType;
1183
1184 return S_OK;
1185}
1186
1187STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1188{
1189 CheckComArgOutPointerValid(aKeyboardHIDType);
1190
1191 AutoCaller autoCaller(this);
1192 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1193
1194 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1195
1196 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1197
1198 return S_OK;
1199}
1200
1201STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1202{
1203 AutoCaller autoCaller(this);
1204 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1205 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1206
1207 HRESULT rc = checkStateDependency(MutableStateDep);
1208 if (FAILED(rc)) return rc;
1209
1210 setModified(IsModified_MachineData);
1211 mHWData.backup();
1212 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1213
1214 return S_OK;
1215}
1216
1217STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1218{
1219 CheckComArgOutPointerValid(aPointingHIDType);
1220
1221 AutoCaller autoCaller(this);
1222 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1223
1224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1225
1226 *aPointingHIDType = mHWData->mPointingHIDType;
1227
1228 return S_OK;
1229}
1230
1231STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1232{
1233 AutoCaller autoCaller(this);
1234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1236
1237 HRESULT rc = checkStateDependency(MutableStateDep);
1238 if (FAILED(rc)) return rc;
1239
1240 setModified(IsModified_MachineData);
1241 mHWData.backup();
1242 mHWData->mPointingHIDType = aPointingHIDType;
1243
1244 return S_OK;
1245}
1246
1247STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1248{
1249 CheckComArgOutPointerValid(aChipsetType);
1250
1251 AutoCaller autoCaller(this);
1252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1253
1254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1255
1256 *aChipsetType = mHWData->mChipsetType;
1257
1258 return S_OK;
1259}
1260
1261STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1262{
1263 AutoCaller autoCaller(this);
1264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1266
1267 HRESULT rc = checkStateDependency(MutableStateDep);
1268 if (FAILED(rc)) return rc;
1269
1270 if (aChipsetType != mHWData->mChipsetType)
1271 {
1272 setModified(IsModified_MachineData);
1273 mHWData.backup();
1274 mHWData->mChipsetType = aChipsetType;
1275
1276 // Resize network adapter array, to be finalized on commit/rollback.
1277 // We must not throw away entries yet, otherwise settings are lost
1278 // without a way to roll back.
1279 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1280 uint32_t oldCount = mNetworkAdapters.size();
1281 if (newCount > oldCount)
1282 {
1283 mNetworkAdapters.resize(newCount);
1284 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1285 {
1286 unconst(mNetworkAdapters[slot]).createObject();
1287 mNetworkAdapters[slot]->init(this, slot);
1288 }
1289 }
1290 }
1291
1292 return S_OK;
1293}
1294
1295STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1296{
1297 CheckComArgOutPointerValid(aHWVersion);
1298
1299 AutoCaller autoCaller(this);
1300 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1301
1302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1303
1304 mHWData->mHWVersion.cloneTo(aHWVersion);
1305
1306 return S_OK;
1307}
1308
1309STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1310{
1311 /* check known version */
1312 Utf8Str hwVersion = aHWVersion;
1313 if ( hwVersion.compare("1") != 0
1314 && hwVersion.compare("2") != 0)
1315 return setError(E_INVALIDARG,
1316 tr("Invalid hardware version: %ls\n"), aHWVersion);
1317
1318 AutoCaller autoCaller(this);
1319 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1320
1321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1322
1323 HRESULT rc = checkStateDependency(MutableStateDep);
1324 if (FAILED(rc)) return rc;
1325
1326 setModified(IsModified_MachineData);
1327 mHWData.backup();
1328 mHWData->mHWVersion = hwVersion;
1329
1330 return S_OK;
1331}
1332
1333STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1334{
1335 CheckComArgOutPointerValid(aUUID);
1336
1337 AutoCaller autoCaller(this);
1338 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1339
1340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1341
1342 if (mHWData->mHardwareUUID.isValid())
1343 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1344 else
1345 mData->mUuid.toUtf16().cloneTo(aUUID);
1346
1347 return S_OK;
1348}
1349
1350STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1351{
1352 Guid hardwareUUID(aUUID);
1353 if (!hardwareUUID.isValid())
1354 return E_INVALIDARG;
1355
1356 AutoCaller autoCaller(this);
1357 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1358
1359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1360
1361 HRESULT rc = checkStateDependency(MutableStateDep);
1362 if (FAILED(rc)) return rc;
1363
1364 setModified(IsModified_MachineData);
1365 mHWData.backup();
1366 if (hardwareUUID == mData->mUuid)
1367 mHWData->mHardwareUUID.clear();
1368 else
1369 mHWData->mHardwareUUID = hardwareUUID;
1370
1371 return S_OK;
1372}
1373
1374STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1375{
1376 CheckComArgOutPointerValid(memorySize);
1377
1378 AutoCaller autoCaller(this);
1379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1380
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 *memorySize = mHWData->mMemorySize;
1384
1385 return S_OK;
1386}
1387
1388STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1389{
1390 /* check RAM limits */
1391 if ( memorySize < MM_RAM_MIN_IN_MB
1392 || memorySize > MM_RAM_MAX_IN_MB
1393 )
1394 return setError(E_INVALIDARG,
1395 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1396 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1397
1398 AutoCaller autoCaller(this);
1399 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1400
1401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1402
1403 HRESULT rc = checkStateDependency(MutableStateDep);
1404 if (FAILED(rc)) return rc;
1405
1406 setModified(IsModified_MachineData);
1407 mHWData.backup();
1408 mHWData->mMemorySize = memorySize;
1409
1410 return S_OK;
1411}
1412
1413STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1414{
1415 CheckComArgOutPointerValid(CPUCount);
1416
1417 AutoCaller autoCaller(this);
1418 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1419
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 *CPUCount = mHWData->mCPUCount;
1423
1424 return S_OK;
1425}
1426
1427STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1428{
1429 /* check CPU limits */
1430 if ( CPUCount < SchemaDefs::MinCPUCount
1431 || CPUCount > SchemaDefs::MaxCPUCount
1432 )
1433 return setError(E_INVALIDARG,
1434 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1435 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1436
1437 AutoCaller autoCaller(this);
1438 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1439
1440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1443 if (mHWData->mCPUHotPlugEnabled)
1444 {
1445 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1446 {
1447 if (mHWData->mCPUAttached[idx])
1448 return setError(E_INVALIDARG,
1449 tr("There is still a CPU attached to socket %lu."
1450 "Detach the CPU before removing the socket"),
1451 CPUCount, idx+1);
1452 }
1453 }
1454
1455 HRESULT rc = checkStateDependency(MutableStateDep);
1456 if (FAILED(rc)) return rc;
1457
1458 setModified(IsModified_MachineData);
1459 mHWData.backup();
1460 mHWData->mCPUCount = CPUCount;
1461
1462 return S_OK;
1463}
1464
1465STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1466{
1467 CheckComArgOutPointerValid(aExecutionCap);
1468
1469 AutoCaller autoCaller(this);
1470 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1471
1472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
1474 *aExecutionCap = mHWData->mCpuExecutionCap;
1475
1476 return S_OK;
1477}
1478
1479STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1480{
1481 HRESULT rc = S_OK;
1482
1483 /* check throttle limits */
1484 if ( aExecutionCap < 1
1485 || aExecutionCap > 100
1486 )
1487 return setError(E_INVALIDARG,
1488 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1489 aExecutionCap, 1, 100);
1490
1491 AutoCaller autoCaller(this);
1492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1493
1494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1495
1496 alock.release();
1497 rc = onCPUExecutionCapChange(aExecutionCap);
1498 alock.acquire();
1499 if (FAILED(rc)) return rc;
1500
1501 setModified(IsModified_MachineData);
1502 mHWData.backup();
1503 mHWData->mCpuExecutionCap = aExecutionCap;
1504
1505 /* Save settings if online - todo why is this required?? */
1506 if (Global::IsOnline(mData->mMachineState))
1507 saveSettings(NULL);
1508
1509 return S_OK;
1510}
1511
1512
1513STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1514{
1515 CheckComArgOutPointerValid(enabled);
1516
1517 AutoCaller autoCaller(this);
1518 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1519
1520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1521
1522 *enabled = mHWData->mCPUHotPlugEnabled;
1523
1524 return S_OK;
1525}
1526
1527STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1528{
1529 HRESULT rc = S_OK;
1530
1531 AutoCaller autoCaller(this);
1532 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 rc = checkStateDependency(MutableStateDep);
1537 if (FAILED(rc)) return rc;
1538
1539 if (mHWData->mCPUHotPlugEnabled != enabled)
1540 {
1541 if (enabled)
1542 {
1543 setModified(IsModified_MachineData);
1544 mHWData.backup();
1545
1546 /* Add the amount of CPUs currently attached */
1547 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1548 {
1549 mHWData->mCPUAttached[i] = true;
1550 }
1551 }
1552 else
1553 {
1554 /*
1555 * We can disable hotplug only if the amount of maximum CPUs is equal
1556 * to the amount of attached CPUs
1557 */
1558 unsigned cCpusAttached = 0;
1559 unsigned iHighestId = 0;
1560
1561 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1562 {
1563 if (mHWData->mCPUAttached[i])
1564 {
1565 cCpusAttached++;
1566 iHighestId = i;
1567 }
1568 }
1569
1570 if ( (cCpusAttached != mHWData->mCPUCount)
1571 || (iHighestId >= mHWData->mCPUCount))
1572 return setError(E_INVALIDARG,
1573 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1574
1575 setModified(IsModified_MachineData);
1576 mHWData.backup();
1577 }
1578 }
1579
1580 mHWData->mCPUHotPlugEnabled = enabled;
1581
1582 return rc;
1583}
1584
1585STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1586{
1587#ifdef VBOX_WITH_USB_CARDREADER
1588 CheckComArgOutPointerValid(enabled);
1589
1590 AutoCaller autoCaller(this);
1591 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1592
1593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1596
1597 return S_OK;
1598#else
1599 NOREF(enabled);
1600 return E_NOTIMPL;
1601#endif
1602}
1603
1604STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1605{
1606#ifdef VBOX_WITH_USB_CARDREADER
1607 AutoCaller autoCaller(this);
1608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1610
1611 HRESULT rc = checkStateDependency(MutableStateDep);
1612 if (FAILED(rc)) return rc;
1613
1614 setModified(IsModified_MachineData);
1615 mHWData.backup();
1616 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1617
1618 return S_OK;
1619#else
1620 NOREF(enabled);
1621 return E_NOTIMPL;
1622#endif
1623}
1624
1625STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1626{
1627 NOREF(enabled);
1628 return E_NOTIMPL;
1629}
1630
1631STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1632{
1633 NOREF(enabled);
1634 return E_NOTIMPL;
1635}
1636
1637STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1638{
1639 CheckComArgOutPointerValid(enabled);
1640
1641 AutoCaller autoCaller(this);
1642 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1644
1645 *enabled = mHWData->mHPETEnabled;
1646
1647 return S_OK;
1648}
1649
1650STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1651{
1652 HRESULT rc = S_OK;
1653
1654 AutoCaller autoCaller(this);
1655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657
1658 rc = checkStateDependency(MutableStateDep);
1659 if (FAILED(rc)) return rc;
1660
1661 setModified(IsModified_MachineData);
1662 mHWData.backup();
1663
1664 mHWData->mHPETEnabled = enabled;
1665
1666 return rc;
1667}
1668
1669STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1670{
1671 AutoCaller autoCaller(this);
1672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1673
1674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1675
1676 *fEnabled = mHWData->mVideoCaptureEnabled;
1677 return S_OK;
1678}
1679
1680STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1681{
1682 AutoCaller autoCaller(this);
1683 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1684
1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1686 mHWData->mVideoCaptureEnabled = fEnabled;
1687 return S_OK;
1688}
1689
1690STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1691{
1692 AutoCaller autoCaller(this);
1693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1694
1695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1696 mHWData->mVideoCaptureFile.cloneTo(apFile);
1697 return S_OK;
1698}
1699
1700STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1701{
1702 Utf8Str strFile(aFile);
1703 AutoCaller autoCaller(this);
1704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1705
1706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1707 if (strFile.isEmpty())
1708 strFile = "VideoCap.webm";
1709 mHWData->mVideoCaptureFile = strFile;
1710 return S_OK;
1711}
1712
1713
1714STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1715{
1716 AutoCaller autoCaller(this);
1717 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1718
1719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1720 *ulHorzRes = mHWData->mVideoCaptureWidth;
1721 return S_OK;
1722}
1723
1724STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1725{
1726 AutoCaller autoCaller(this);
1727 if (FAILED(autoCaller.rc()))
1728 {
1729 LogFlow(("Autolocked failed\n"));
1730 return autoCaller.rc();
1731 }
1732
1733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1734 mHWData->mVideoCaptureWidth = ulHorzRes;
1735 return S_OK;
1736}
1737
1738STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1739{
1740 AutoCaller autoCaller(this);
1741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1742
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744 *ulVertRes = mHWData->mVideoCaptureHeight;
1745 return S_OK;
1746}
1747
1748STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1749{
1750 AutoCaller autoCaller(this);
1751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1752
1753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1754 mHWData->mVideoCaptureHeight = ulVertRes;
1755 return S_OK;
1756}
1757
1758STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1759{
1760 CheckComArgOutPointerValid(memorySize);
1761
1762 AutoCaller autoCaller(this);
1763 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1764
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766
1767 *memorySize = mHWData->mVRAMSize;
1768
1769 return S_OK;
1770}
1771
1772STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1773{
1774 /* check VRAM limits */
1775 if (memorySize < SchemaDefs::MinGuestVRAM ||
1776 memorySize > SchemaDefs::MaxGuestVRAM)
1777 return setError(E_INVALIDARG,
1778 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1779 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1780
1781 AutoCaller autoCaller(this);
1782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1783
1784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1785
1786 HRESULT rc = checkStateDependency(MutableStateDep);
1787 if (FAILED(rc)) return rc;
1788
1789 setModified(IsModified_MachineData);
1790 mHWData.backup();
1791 mHWData->mVRAMSize = memorySize;
1792
1793 return S_OK;
1794}
1795
1796/** @todo this method should not be public */
1797STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1798{
1799 CheckComArgOutPointerValid(memoryBalloonSize);
1800
1801 AutoCaller autoCaller(this);
1802 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1803
1804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1805
1806 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1807
1808 return S_OK;
1809}
1810
1811/**
1812 * Set the memory balloon size.
1813 *
1814 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1815 * we have to make sure that we never call IGuest from here.
1816 */
1817STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1818{
1819 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1820#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1821 /* check limits */
1822 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1823 return setError(E_INVALIDARG,
1824 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1825 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1826
1827 AutoCaller autoCaller(this);
1828 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1829
1830 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1831
1832 setModified(IsModified_MachineData);
1833 mHWData.backup();
1834 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1835
1836 return S_OK;
1837#else
1838 NOREF(memoryBalloonSize);
1839 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1840#endif
1841}
1842
1843STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1844{
1845 CheckComArgOutPointerValid(enabled);
1846
1847 AutoCaller autoCaller(this);
1848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1849
1850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1851
1852 *enabled = mHWData->mPageFusionEnabled;
1853 return S_OK;
1854}
1855
1856STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1857{
1858#ifdef VBOX_WITH_PAGE_SHARING
1859 AutoCaller autoCaller(this);
1860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1861
1862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1865 setModified(IsModified_MachineData);
1866 mHWData.backup();
1867 mHWData->mPageFusionEnabled = enabled;
1868 return S_OK;
1869#else
1870 NOREF(enabled);
1871 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1872#endif
1873}
1874
1875STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1876{
1877 CheckComArgOutPointerValid(enabled);
1878
1879 AutoCaller autoCaller(this);
1880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1881
1882 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1883
1884 *enabled = mHWData->mAccelerate3DEnabled;
1885
1886 return S_OK;
1887}
1888
1889STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1890{
1891 AutoCaller autoCaller(this);
1892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1893
1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1895
1896 HRESULT rc = checkStateDependency(MutableStateDep);
1897 if (FAILED(rc)) return rc;
1898
1899 /** @todo check validity! */
1900
1901 setModified(IsModified_MachineData);
1902 mHWData.backup();
1903 mHWData->mAccelerate3DEnabled = enable;
1904
1905 return S_OK;
1906}
1907
1908
1909STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1910{
1911 CheckComArgOutPointerValid(enabled);
1912
1913 AutoCaller autoCaller(this);
1914 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1915
1916 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1917
1918 *enabled = mHWData->mAccelerate2DVideoEnabled;
1919
1920 return S_OK;
1921}
1922
1923STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1924{
1925 AutoCaller autoCaller(this);
1926 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1927
1928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1929
1930 HRESULT rc = checkStateDependency(MutableStateDep);
1931 if (FAILED(rc)) return rc;
1932
1933 /** @todo check validity! */
1934
1935 setModified(IsModified_MachineData);
1936 mHWData.backup();
1937 mHWData->mAccelerate2DVideoEnabled = enable;
1938
1939 return S_OK;
1940}
1941
1942STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1943{
1944 CheckComArgOutPointerValid(monitorCount);
1945
1946 AutoCaller autoCaller(this);
1947 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1948
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950
1951 *monitorCount = mHWData->mMonitorCount;
1952
1953 return S_OK;
1954}
1955
1956STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1957{
1958 /* make sure monitor count is a sensible number */
1959 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1960 return setError(E_INVALIDARG,
1961 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1962 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1963
1964 AutoCaller autoCaller(this);
1965 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1966
1967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1968
1969 HRESULT rc = checkStateDependency(MutableStateDep);
1970 if (FAILED(rc)) return rc;
1971
1972 setModified(IsModified_MachineData);
1973 mHWData.backup();
1974 mHWData->mMonitorCount = monitorCount;
1975
1976 return S_OK;
1977}
1978
1979STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1980{
1981 CheckComArgOutPointerValid(biosSettings);
1982
1983 AutoCaller autoCaller(this);
1984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1985
1986 /* mBIOSSettings is constant during life time, no need to lock */
1987 mBIOSSettings.queryInterfaceTo(biosSettings);
1988
1989 return S_OK;
1990}
1991
1992STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1993{
1994 CheckComArgOutPointerValid(aVal);
1995
1996 AutoCaller autoCaller(this);
1997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1998
1999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2000
2001 switch(property)
2002 {
2003 case CPUPropertyType_PAE:
2004 *aVal = mHWData->mPAEEnabled;
2005 break;
2006
2007 case CPUPropertyType_Synthetic:
2008 *aVal = mHWData->mSyntheticCpu;
2009 break;
2010
2011 default:
2012 return E_INVALIDARG;
2013 }
2014 return S_OK;
2015}
2016
2017STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2018{
2019 AutoCaller autoCaller(this);
2020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2021
2022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2023
2024 HRESULT rc = checkStateDependency(MutableStateDep);
2025 if (FAILED(rc)) return rc;
2026
2027 switch(property)
2028 {
2029 case CPUPropertyType_PAE:
2030 setModified(IsModified_MachineData);
2031 mHWData.backup();
2032 mHWData->mPAEEnabled = !!aVal;
2033 break;
2034
2035 case CPUPropertyType_Synthetic:
2036 setModified(IsModified_MachineData);
2037 mHWData.backup();
2038 mHWData->mSyntheticCpu = !!aVal;
2039 break;
2040
2041 default:
2042 return E_INVALIDARG;
2043 }
2044 return S_OK;
2045}
2046
2047STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2048{
2049 CheckComArgOutPointerValid(aValEax);
2050 CheckComArgOutPointerValid(aValEbx);
2051 CheckComArgOutPointerValid(aValEcx);
2052 CheckComArgOutPointerValid(aValEdx);
2053
2054 AutoCaller autoCaller(this);
2055 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2056
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 switch(aId)
2060 {
2061 case 0x0:
2062 case 0x1:
2063 case 0x2:
2064 case 0x3:
2065 case 0x4:
2066 case 0x5:
2067 case 0x6:
2068 case 0x7:
2069 case 0x8:
2070 case 0x9:
2071 case 0xA:
2072 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2073 return E_INVALIDARG;
2074
2075 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2076 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2077 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2078 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2079 break;
2080
2081 case 0x80000000:
2082 case 0x80000001:
2083 case 0x80000002:
2084 case 0x80000003:
2085 case 0x80000004:
2086 case 0x80000005:
2087 case 0x80000006:
2088 case 0x80000007:
2089 case 0x80000008:
2090 case 0x80000009:
2091 case 0x8000000A:
2092 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2093 return E_INVALIDARG;
2094
2095 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2096 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2097 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2098 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2099 break;
2100
2101 default:
2102 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2103 }
2104 return S_OK;
2105}
2106
2107STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2108{
2109 AutoCaller autoCaller(this);
2110 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2111
2112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2113
2114 HRESULT rc = checkStateDependency(MutableStateDep);
2115 if (FAILED(rc)) return rc;
2116
2117 switch(aId)
2118 {
2119 case 0x0:
2120 case 0x1:
2121 case 0x2:
2122 case 0x3:
2123 case 0x4:
2124 case 0x5:
2125 case 0x6:
2126 case 0x7:
2127 case 0x8:
2128 case 0x9:
2129 case 0xA:
2130 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2131 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2132 setModified(IsModified_MachineData);
2133 mHWData.backup();
2134 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2135 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2136 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2137 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2138 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2139 break;
2140
2141 case 0x80000000:
2142 case 0x80000001:
2143 case 0x80000002:
2144 case 0x80000003:
2145 case 0x80000004:
2146 case 0x80000005:
2147 case 0x80000006:
2148 case 0x80000007:
2149 case 0x80000008:
2150 case 0x80000009:
2151 case 0x8000000A:
2152 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2153 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2154 setModified(IsModified_MachineData);
2155 mHWData.backup();
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2157 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2158 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2159 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2160 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2161 break;
2162
2163 default:
2164 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2165 }
2166 return S_OK;
2167}
2168
2169STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2170{
2171 AutoCaller autoCaller(this);
2172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2173
2174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2175
2176 HRESULT rc = checkStateDependency(MutableStateDep);
2177 if (FAILED(rc)) return rc;
2178
2179 switch(aId)
2180 {
2181 case 0x0:
2182 case 0x1:
2183 case 0x2:
2184 case 0x3:
2185 case 0x4:
2186 case 0x5:
2187 case 0x6:
2188 case 0x7:
2189 case 0x8:
2190 case 0x9:
2191 case 0xA:
2192 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2193 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2194 setModified(IsModified_MachineData);
2195 mHWData.backup();
2196 /* Invalidate leaf. */
2197 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2198 break;
2199
2200 case 0x80000000:
2201 case 0x80000001:
2202 case 0x80000002:
2203 case 0x80000003:
2204 case 0x80000004:
2205 case 0x80000005:
2206 case 0x80000006:
2207 case 0x80000007:
2208 case 0x80000008:
2209 case 0x80000009:
2210 case 0x8000000A:
2211 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2212 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2213 setModified(IsModified_MachineData);
2214 mHWData.backup();
2215 /* Invalidate leaf. */
2216 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2217 break;
2218
2219 default:
2220 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2221 }
2222 return S_OK;
2223}
2224
2225STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2226{
2227 AutoCaller autoCaller(this);
2228 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2229
2230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2231
2232 HRESULT rc = checkStateDependency(MutableStateDep);
2233 if (FAILED(rc)) return rc;
2234
2235 setModified(IsModified_MachineData);
2236 mHWData.backup();
2237
2238 /* Invalidate all standard leafs. */
2239 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2240 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2241
2242 /* Invalidate all extended leafs. */
2243 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2244 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2245
2246 return S_OK;
2247}
2248
2249STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2250{
2251 CheckComArgOutPointerValid(aVal);
2252
2253 AutoCaller autoCaller(this);
2254 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2255
2256 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2257
2258 switch(property)
2259 {
2260 case HWVirtExPropertyType_Enabled:
2261 *aVal = mHWData->mHWVirtExEnabled;
2262 break;
2263
2264 case HWVirtExPropertyType_Exclusive:
2265 *aVal = mHWData->mHWVirtExExclusive;
2266 break;
2267
2268 case HWVirtExPropertyType_VPID:
2269 *aVal = mHWData->mHWVirtExVPIDEnabled;
2270 break;
2271
2272 case HWVirtExPropertyType_NestedPaging:
2273 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2274 break;
2275
2276 case HWVirtExPropertyType_LargePages:
2277 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2278#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2279 *aVal = FALSE;
2280#endif
2281 break;
2282
2283 case HWVirtExPropertyType_Force:
2284 *aVal = mHWData->mHWVirtExForceEnabled;
2285 break;
2286
2287 default:
2288 return E_INVALIDARG;
2289 }
2290 return S_OK;
2291}
2292
2293STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2294{
2295 AutoCaller autoCaller(this);
2296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2297
2298 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2299
2300 HRESULT rc = checkStateDependency(MutableStateDep);
2301 if (FAILED(rc)) return rc;
2302
2303 switch(property)
2304 {
2305 case HWVirtExPropertyType_Enabled:
2306 setModified(IsModified_MachineData);
2307 mHWData.backup();
2308 mHWData->mHWVirtExEnabled = !!aVal;
2309 break;
2310
2311 case HWVirtExPropertyType_Exclusive:
2312 setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mHWVirtExExclusive = !!aVal;
2315 break;
2316
2317 case HWVirtExPropertyType_VPID:
2318 setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2321 break;
2322
2323 case HWVirtExPropertyType_NestedPaging:
2324 setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2327 break;
2328
2329 case HWVirtExPropertyType_LargePages:
2330 setModified(IsModified_MachineData);
2331 mHWData.backup();
2332 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2333 break;
2334
2335 case HWVirtExPropertyType_Force:
2336 setModified(IsModified_MachineData);
2337 mHWData.backup();
2338 mHWData->mHWVirtExForceEnabled = !!aVal;
2339 break;
2340
2341 default:
2342 return E_INVALIDARG;
2343 }
2344
2345 return S_OK;
2346}
2347
2348STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2349{
2350 CheckComArgOutPointerValid(aSnapshotFolder);
2351
2352 AutoCaller autoCaller(this);
2353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2354
2355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2356
2357 Utf8Str strFullSnapshotFolder;
2358 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2359 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2360
2361 return S_OK;
2362}
2363
2364STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2365{
2366 /* @todo (r=dmik):
2367 * 1. Allow to change the name of the snapshot folder containing snapshots
2368 * 2. Rename the folder on disk instead of just changing the property
2369 * value (to be smart and not to leave garbage). Note that it cannot be
2370 * done here because the change may be rolled back. Thus, the right
2371 * place is #saveSettings().
2372 */
2373
2374 AutoCaller autoCaller(this);
2375 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2376
2377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2378
2379 HRESULT rc = checkStateDependency(MutableStateDep);
2380 if (FAILED(rc)) return rc;
2381
2382 if (!mData->mCurrentSnapshot.isNull())
2383 return setError(E_FAIL,
2384 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2385
2386 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2387
2388 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2389 if (strSnapshotFolder.isEmpty())
2390 strSnapshotFolder = "Snapshots";
2391 int vrc = calculateFullPath(strSnapshotFolder,
2392 strSnapshotFolder);
2393 if (RT_FAILURE(vrc))
2394 return setError(E_FAIL,
2395 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2396 aSnapshotFolder, vrc);
2397
2398 setModified(IsModified_MachineData);
2399 mUserData.backup();
2400
2401 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2402
2403 return S_OK;
2404}
2405
2406STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2407{
2408 CheckComArgOutSafeArrayPointerValid(aAttachments);
2409
2410 AutoCaller autoCaller(this);
2411 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2412
2413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2414
2415 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2416 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2417
2418 return S_OK;
2419}
2420
2421STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2422{
2423 CheckComArgOutPointerValid(vrdeServer);
2424
2425 AutoCaller autoCaller(this);
2426 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2427
2428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2429
2430 Assert(!!mVRDEServer);
2431 mVRDEServer.queryInterfaceTo(vrdeServer);
2432
2433 return S_OK;
2434}
2435
2436STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2437{
2438 CheckComArgOutPointerValid(audioAdapter);
2439
2440 AutoCaller autoCaller(this);
2441 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2442
2443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2444
2445 mAudioAdapter.queryInterfaceTo(audioAdapter);
2446 return S_OK;
2447}
2448
2449STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2450{
2451#ifdef VBOX_WITH_VUSB
2452 CheckComArgOutPointerValid(aUSBController);
2453
2454 AutoCaller autoCaller(this);
2455 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2456
2457 clearError();
2458 MultiResult rc(S_OK);
2459
2460# ifdef VBOX_WITH_USB
2461 rc = mParent->host()->checkUSBProxyService();
2462 if (FAILED(rc)) return rc;
2463# endif
2464
2465 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2466
2467 return rc = mUSBController.queryInterfaceTo(aUSBController);
2468#else
2469 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2470 * extended error info to indicate that USB is simply not available
2471 * (w/o treating it as a failure), for example, as in OSE */
2472 NOREF(aUSBController);
2473 ReturnComNotImplemented();
2474#endif /* VBOX_WITH_VUSB */
2475}
2476
2477STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2478{
2479 CheckComArgOutPointerValid(aFilePath);
2480
2481 AutoLimitedCaller autoCaller(this);
2482 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2483
2484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 mData->m_strConfigFileFull.cloneTo(aFilePath);
2487 return S_OK;
2488}
2489
2490STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2491{
2492 CheckComArgOutPointerValid(aModified);
2493
2494 AutoCaller autoCaller(this);
2495 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2496
2497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2498
2499 HRESULT rc = checkStateDependency(MutableStateDep);
2500 if (FAILED(rc)) return rc;
2501
2502 if (!mData->pMachineConfigFile->fileExists())
2503 // this is a new machine, and no config file exists yet:
2504 *aModified = TRUE;
2505 else
2506 *aModified = (mData->flModifications != 0);
2507
2508 return S_OK;
2509}
2510
2511STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2512{
2513 CheckComArgOutPointerValid(aSessionState);
2514
2515 AutoCaller autoCaller(this);
2516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2517
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 *aSessionState = mData->mSession.mState;
2521
2522 return S_OK;
2523}
2524
2525STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2526{
2527 CheckComArgOutPointerValid(aSessionType);
2528
2529 AutoCaller autoCaller(this);
2530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2531
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 mData->mSession.mType.cloneTo(aSessionType);
2535
2536 return S_OK;
2537}
2538
2539STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2540{
2541 CheckComArgOutPointerValid(aSessionPID);
2542
2543 AutoCaller autoCaller(this);
2544 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2545
2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2547
2548 *aSessionPID = mData->mSession.mPID;
2549
2550 return S_OK;
2551}
2552
2553STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2554{
2555 CheckComArgOutPointerValid(machineState);
2556
2557 AutoCaller autoCaller(this);
2558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2559
2560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2561
2562 *machineState = mData->mMachineState;
2563
2564 return S_OK;
2565}
2566
2567STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2568{
2569 CheckComArgOutPointerValid(aLastStateChange);
2570
2571 AutoCaller autoCaller(this);
2572 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2573
2574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2575
2576 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2577
2578 return S_OK;
2579}
2580
2581STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2582{
2583 CheckComArgOutPointerValid(aStateFilePath);
2584
2585 AutoCaller autoCaller(this);
2586 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2587
2588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2589
2590 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2591
2592 return S_OK;
2593}
2594
2595STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2596{
2597 CheckComArgOutPointerValid(aLogFolder);
2598
2599 AutoCaller autoCaller(this);
2600 AssertComRCReturnRC(autoCaller.rc());
2601
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 Utf8Str logFolder;
2605 getLogFolder(logFolder);
2606 logFolder.cloneTo(aLogFolder);
2607
2608 return S_OK;
2609}
2610
2611STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2612{
2613 CheckComArgOutPointerValid(aCurrentSnapshot);
2614
2615 AutoCaller autoCaller(this);
2616 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2617
2618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2619
2620 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2621
2622 return S_OK;
2623}
2624
2625STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2626{
2627 CheckComArgOutPointerValid(aSnapshotCount);
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2635 ? 0
2636 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2637
2638 return S_OK;
2639}
2640
2641STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2642{
2643 CheckComArgOutPointerValid(aCurrentStateModified);
2644
2645 AutoCaller autoCaller(this);
2646 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2647
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 /* Note: for machines with no snapshots, we always return FALSE
2651 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2652 * reasons :) */
2653
2654 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2655 ? FALSE
2656 : mData->mCurrentStateModified;
2657
2658 return S_OK;
2659}
2660
2661STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2662{
2663 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2664
2665 AutoCaller autoCaller(this);
2666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2667
2668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2669
2670 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2671 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2672
2673 return S_OK;
2674}
2675
2676STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2677{
2678 CheckComArgOutPointerValid(aClipboardMode);
2679
2680 AutoCaller autoCaller(this);
2681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2682
2683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 *aClipboardMode = mHWData->mClipboardMode;
2686
2687 return S_OK;
2688}
2689
2690STDMETHODIMP
2691Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2692{
2693 HRESULT rc = S_OK;
2694
2695 AutoCaller autoCaller(this);
2696 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2697
2698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2699
2700 alock.release();
2701 rc = onClipboardModeChange(aClipboardMode);
2702 alock.acquire();
2703 if (FAILED(rc)) return rc;
2704
2705 setModified(IsModified_MachineData);
2706 mHWData.backup();
2707 mHWData->mClipboardMode = aClipboardMode;
2708
2709 /* Save settings if online - todo why is this required?? */
2710 if (Global::IsOnline(mData->mMachineState))
2711 saveSettings(NULL);
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2717{
2718 CheckComArgOutPointerValid(aDragAndDropMode);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 *aDragAndDropMode = mHWData->mDragAndDropMode;
2726
2727 return S_OK;
2728}
2729
2730STDMETHODIMP
2731Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2732{
2733 HRESULT rc = S_OK;
2734
2735 AutoCaller autoCaller(this);
2736 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2737
2738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 alock.release();
2741 rc = onDragAndDropModeChange(aDragAndDropMode);
2742 alock.acquire();
2743 if (FAILED(rc)) return rc;
2744
2745 setModified(IsModified_MachineData);
2746 mHWData.backup();
2747 mHWData->mDragAndDropMode = aDragAndDropMode;
2748
2749 /* Save settings if online - todo why is this required?? */
2750 if (Global::IsOnline(mData->mMachineState))
2751 saveSettings(NULL);
2752
2753 return S_OK;
2754}
2755
2756STDMETHODIMP
2757Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2758{
2759 CheckComArgOutPointerValid(aPatterns);
2760
2761 AutoCaller autoCaller(this);
2762 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2763
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765
2766 try
2767 {
2768 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2769 }
2770 catch (...)
2771 {
2772 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2773 }
2774
2775 return S_OK;
2776}
2777
2778STDMETHODIMP
2779Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2780{
2781 AutoCaller autoCaller(this);
2782 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2783
2784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2785
2786 HRESULT rc = checkStateDependency(MutableStateDep);
2787 if (FAILED(rc)) return rc;
2788
2789 setModified(IsModified_MachineData);
2790 mHWData.backup();
2791 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2792 return rc;
2793}
2794
2795STDMETHODIMP
2796Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2797{
2798 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2799
2800 AutoCaller autoCaller(this);
2801 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2802
2803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2804
2805 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2806 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2807
2808 return S_OK;
2809}
2810
2811STDMETHODIMP
2812Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2813{
2814 CheckComArgOutPointerValid(aEnabled);
2815
2816 AutoCaller autoCaller(this);
2817 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2818
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aEnabled = mUserData->s.fTeleporterEnabled;
2822
2823 return S_OK;
2824}
2825
2826STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2827{
2828 AutoCaller autoCaller(this);
2829 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2830
2831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2832
2833 /* Only allow it to be set to true when PoweredOff or Aborted.
2834 (Clearing it is always permitted.) */
2835 if ( aEnabled
2836 && mData->mRegistered
2837 && ( !isSessionMachine()
2838 || ( mData->mMachineState != MachineState_PoweredOff
2839 && mData->mMachineState != MachineState_Teleported
2840 && mData->mMachineState != MachineState_Aborted
2841 )
2842 )
2843 )
2844 return setError(VBOX_E_INVALID_VM_STATE,
2845 tr("The machine is not powered off (state is %s)"),
2846 Global::stringifyMachineState(mData->mMachineState));
2847
2848 setModified(IsModified_MachineData);
2849 mUserData.backup();
2850 mUserData->s.fTeleporterEnabled = !!aEnabled;
2851
2852 return S_OK;
2853}
2854
2855STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2856{
2857 CheckComArgOutPointerValid(aPort);
2858
2859 AutoCaller autoCaller(this);
2860 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2861
2862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2863
2864 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2865
2866 return S_OK;
2867}
2868
2869STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2870{
2871 if (aPort >= _64K)
2872 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2873
2874 AutoCaller autoCaller(this);
2875 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2876
2877 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2878
2879 HRESULT rc = checkStateDependency(MutableStateDep);
2880 if (FAILED(rc)) return rc;
2881
2882 setModified(IsModified_MachineData);
2883 mUserData.backup();
2884 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2885
2886 return S_OK;
2887}
2888
2889STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2890{
2891 CheckComArgOutPointerValid(aAddress);
2892
2893 AutoCaller autoCaller(this);
2894 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2895
2896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2897
2898 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2899
2900 return S_OK;
2901}
2902
2903STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2904{
2905 AutoCaller autoCaller(this);
2906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2907
2908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2909
2910 HRESULT rc = checkStateDependency(MutableStateDep);
2911 if (FAILED(rc)) return rc;
2912
2913 setModified(IsModified_MachineData);
2914 mUserData.backup();
2915 mUserData->s.strTeleporterAddress = aAddress;
2916
2917 return S_OK;
2918}
2919
2920STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2921{
2922 CheckComArgOutPointerValid(aPassword);
2923
2924 AutoCaller autoCaller(this);
2925 HRESULT hrc = autoCaller.rc();
2926 if (SUCCEEDED(hrc))
2927 {
2928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2929 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2930 }
2931
2932 return hrc;
2933}
2934
2935STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2936{
2937 /*
2938 * Hash the password first.
2939 */
2940 Utf8Str strPassword(aPassword);
2941 if (!strPassword.isEmpty())
2942 {
2943 if (VBoxIsPasswordHashed(&strPassword))
2944 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2945 VBoxHashPassword(&strPassword);
2946 }
2947
2948 /*
2949 * Do the update.
2950 */
2951 AutoCaller autoCaller(this);
2952 HRESULT hrc = autoCaller.rc();
2953 if (SUCCEEDED(hrc))
2954 {
2955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2956 hrc = checkStateDependency(MutableStateDep);
2957 if (SUCCEEDED(hrc))
2958 {
2959 setModified(IsModified_MachineData);
2960 mUserData.backup();
2961 mUserData->s.strTeleporterPassword = strPassword;
2962 }
2963 }
2964
2965 return hrc;
2966}
2967
2968STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2969{
2970 CheckComArgOutPointerValid(aState);
2971
2972 AutoCaller autoCaller(this);
2973 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2974
2975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2976
2977 *aState = mUserData->s.enmFaultToleranceState;
2978 return S_OK;
2979}
2980
2981STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2982{
2983 AutoCaller autoCaller(this);
2984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2985
2986 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2987
2988 /* @todo deal with running state change. */
2989 HRESULT rc = checkStateDependency(MutableStateDep);
2990 if (FAILED(rc)) return rc;
2991
2992 setModified(IsModified_MachineData);
2993 mUserData.backup();
2994 mUserData->s.enmFaultToleranceState = aState;
2995 return S_OK;
2996}
2997
2998STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2999{
3000 CheckComArgOutPointerValid(aAddress);
3001
3002 AutoCaller autoCaller(this);
3003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3004
3005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3006
3007 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3008 return S_OK;
3009}
3010
3011STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3012{
3013 AutoCaller autoCaller(this);
3014 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3015
3016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3017
3018 /* @todo deal with running state change. */
3019 HRESULT rc = checkStateDependency(MutableStateDep);
3020 if (FAILED(rc)) return rc;
3021
3022 setModified(IsModified_MachineData);
3023 mUserData.backup();
3024 mUserData->s.strFaultToleranceAddress = aAddress;
3025 return S_OK;
3026}
3027
3028STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3029{
3030 CheckComArgOutPointerValid(aPort);
3031
3032 AutoCaller autoCaller(this);
3033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3034
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 *aPort = mUserData->s.uFaultTolerancePort;
3038 return S_OK;
3039}
3040
3041STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3042{
3043 AutoCaller autoCaller(this);
3044 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3045
3046 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 /* @todo deal with running state change. */
3049 HRESULT rc = checkStateDependency(MutableStateDep);
3050 if (FAILED(rc)) return rc;
3051
3052 setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.uFaultTolerancePort = aPort;
3055 return S_OK;
3056}
3057
3058STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3059{
3060 CheckComArgOutPointerValid(aPassword);
3061
3062 AutoCaller autoCaller(this);
3063 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3064
3065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3066
3067 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3068
3069 return S_OK;
3070}
3071
3072STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3073{
3074 AutoCaller autoCaller(this);
3075 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3076
3077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3078
3079 /* @todo deal with running state change. */
3080 HRESULT rc = checkStateDependency(MutableStateDep);
3081 if (FAILED(rc)) return rc;
3082
3083 setModified(IsModified_MachineData);
3084 mUserData.backup();
3085 mUserData->s.strFaultTolerancePassword = aPassword;
3086
3087 return S_OK;
3088}
3089
3090STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3091{
3092 CheckComArgOutPointerValid(aInterval);
3093
3094 AutoCaller autoCaller(this);
3095 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3096
3097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3098
3099 *aInterval = mUserData->s.uFaultToleranceInterval;
3100 return S_OK;
3101}
3102
3103STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3104{
3105 AutoCaller autoCaller(this);
3106 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3107
3108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3109
3110 /* @todo deal with running state change. */
3111 HRESULT rc = checkStateDependency(MutableStateDep);
3112 if (FAILED(rc)) return rc;
3113
3114 setModified(IsModified_MachineData);
3115 mUserData.backup();
3116 mUserData->s.uFaultToleranceInterval = aInterval;
3117 return S_OK;
3118}
3119
3120STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3121{
3122 CheckComArgOutPointerValid(aEnabled);
3123
3124 AutoCaller autoCaller(this);
3125 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3126
3127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3128
3129 *aEnabled = mUserData->s.fRTCUseUTC;
3130
3131 return S_OK;
3132}
3133
3134STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3135{
3136 AutoCaller autoCaller(this);
3137 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3138
3139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 /* Only allow it to be set to true when PoweredOff or Aborted.
3142 (Clearing it is always permitted.) */
3143 if ( aEnabled
3144 && mData->mRegistered
3145 && ( !isSessionMachine()
3146 || ( mData->mMachineState != MachineState_PoweredOff
3147 && mData->mMachineState != MachineState_Teleported
3148 && mData->mMachineState != MachineState_Aborted
3149 )
3150 )
3151 )
3152 return setError(VBOX_E_INVALID_VM_STATE,
3153 tr("The machine is not powered off (state is %s)"),
3154 Global::stringifyMachineState(mData->mMachineState));
3155
3156 setModified(IsModified_MachineData);
3157 mUserData.backup();
3158 mUserData->s.fRTCUseUTC = !!aEnabled;
3159
3160 return S_OK;
3161}
3162
3163STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3164{
3165 CheckComArgOutPointerValid(aEnabled);
3166
3167 AutoCaller autoCaller(this);
3168 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3169
3170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 *aEnabled = mHWData->mIOCacheEnabled;
3173
3174 return S_OK;
3175}
3176
3177STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3178{
3179 AutoCaller autoCaller(this);
3180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3181
3182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3183
3184 HRESULT rc = checkStateDependency(MutableStateDep);
3185 if (FAILED(rc)) return rc;
3186
3187 setModified(IsModified_MachineData);
3188 mHWData.backup();
3189 mHWData->mIOCacheEnabled = aEnabled;
3190
3191 return S_OK;
3192}
3193
3194STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3195{
3196 CheckComArgOutPointerValid(aIOCacheSize);
3197
3198 AutoCaller autoCaller(this);
3199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3200
3201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3202
3203 *aIOCacheSize = mHWData->mIOCacheSize;
3204
3205 return S_OK;
3206}
3207
3208STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3209{
3210 AutoCaller autoCaller(this);
3211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3212
3213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3214
3215 HRESULT rc = checkStateDependency(MutableStateDep);
3216 if (FAILED(rc)) return rc;
3217
3218 setModified(IsModified_MachineData);
3219 mHWData.backup();
3220 mHWData->mIOCacheSize = aIOCacheSize;
3221
3222 return S_OK;
3223}
3224
3225
3226/**
3227 * @note Locks objects!
3228 */
3229STDMETHODIMP Machine::LockMachine(ISession *aSession,
3230 LockType_T lockType)
3231{
3232 CheckComArgNotNull(aSession);
3233
3234 AutoCaller autoCaller(this);
3235 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3236
3237 /* check the session state */
3238 SessionState_T state;
3239 HRESULT rc = aSession->COMGETTER(State)(&state);
3240 if (FAILED(rc)) return rc;
3241
3242 if (state != SessionState_Unlocked)
3243 return setError(VBOX_E_INVALID_OBJECT_STATE,
3244 tr("The given session is busy"));
3245
3246 // get the client's IInternalSessionControl interface
3247 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3248 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3249 E_INVALIDARG);
3250
3251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3252
3253 if (!mData->mRegistered)
3254 return setError(E_UNEXPECTED,
3255 tr("The machine '%s' is not registered"),
3256 mUserData->s.strName.c_str());
3257
3258 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3259
3260 SessionState_T oldState = mData->mSession.mState;
3261 /* Hack: in case the session is closing and there is a progress object
3262 * which allows waiting for the session to be closed, take the opportunity
3263 * and do a limited wait (max. 1 second). This helps a lot when the system
3264 * is busy and thus session closing can take a little while. */
3265 if ( mData->mSession.mState == SessionState_Unlocking
3266 && mData->mSession.mProgress)
3267 {
3268 alock.release();
3269 mData->mSession.mProgress->WaitForCompletion(1000);
3270 alock.acquire();
3271 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3272 }
3273
3274 // try again now
3275 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3276 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3277 )
3278 {
3279 // OK, share the session... we are now dealing with three processes:
3280 // 1) VBoxSVC (where this code runs);
3281 // 2) process C: the caller's client process (who wants a shared session);
3282 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3283
3284 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3285 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3286 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3287 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3288 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3289
3290 /*
3291 * Release the lock before calling the client process. It's safe here
3292 * since the only thing to do after we get the lock again is to add
3293 * the remote control to the list (which doesn't directly influence
3294 * anything).
3295 */
3296 alock.release();
3297
3298 // get the console of the session holding the write lock (this is a remote call)
3299 ComPtr<IConsole> pConsoleW;
3300 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3301 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3302 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3303 if (FAILED(rc))
3304 // the failure may occur w/o any error info (from RPC), so provide one
3305 return setError(VBOX_E_VM_ERROR,
3306 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3307
3308 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3309
3310 // share the session machine and W's console with the caller's session
3311 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3312 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3313 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3314
3315 if (FAILED(rc))
3316 // the failure may occur w/o any error info (from RPC), so provide one
3317 return setError(VBOX_E_VM_ERROR,
3318 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3319 alock.acquire();
3320
3321 // need to revalidate the state after acquiring the lock again
3322 if (mData->mSession.mState != SessionState_Locked)
3323 {
3324 pSessionControl->Uninitialize();
3325 return setError(VBOX_E_INVALID_SESSION_STATE,
3326 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3327 mUserData->s.strName.c_str());
3328 }
3329
3330 // add the caller's session to the list
3331 mData->mSession.mRemoteControls.push_back(pSessionControl);
3332 }
3333 else if ( mData->mSession.mState == SessionState_Locked
3334 || mData->mSession.mState == SessionState_Unlocking
3335 )
3336 {
3337 // sharing not permitted, or machine still unlocking:
3338 return setError(VBOX_E_INVALID_OBJECT_STATE,
3339 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3340 mUserData->s.strName.c_str());
3341 }
3342 else
3343 {
3344 // machine is not locked: then write-lock the machine (create the session machine)
3345
3346 // must not be busy
3347 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3348
3349 // get the caller's session PID
3350 RTPROCESS pid = NIL_RTPROCESS;
3351 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3352 pSessionControl->GetPID((ULONG*)&pid);
3353 Assert(pid != NIL_RTPROCESS);
3354
3355 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3356
3357 if (fLaunchingVMProcess)
3358 {
3359 // this machine is awaiting for a spawning session to be opened:
3360 // then the calling process must be the one that got started by
3361 // LaunchVMProcess()
3362
3363 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3364 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3365
3366 if (mData->mSession.mPID != pid)
3367 return setError(E_ACCESSDENIED,
3368 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3369 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3370 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3371 }
3372
3373 // create the mutable SessionMachine from the current machine
3374 ComObjPtr<SessionMachine> sessionMachine;
3375 sessionMachine.createObject();
3376 rc = sessionMachine->init(this);
3377 AssertComRC(rc);
3378
3379 /* NOTE: doing return from this function after this point but
3380 * before the end is forbidden since it may call SessionMachine::uninit()
3381 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3382 * lock while still holding the Machine lock in alock so that a deadlock
3383 * is possible due to the wrong lock order. */
3384
3385 if (SUCCEEDED(rc))
3386 {
3387 /*
3388 * Set the session state to Spawning to protect against subsequent
3389 * attempts to open a session and to unregister the machine after
3390 * we release the lock.
3391 */
3392 SessionState_T origState = mData->mSession.mState;
3393 mData->mSession.mState = SessionState_Spawning;
3394
3395 /*
3396 * Release the lock before calling the client process -- it will call
3397 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3398 * because the state is Spawning, so that LaunchVMProcess() and
3399 * LockMachine() calls will fail. This method, called before we
3400 * acquire the lock again, will fail because of the wrong PID.
3401 *
3402 * Note that mData->mSession.mRemoteControls accessed outside
3403 * the lock may not be modified when state is Spawning, so it's safe.
3404 */
3405 alock.release();
3406
3407 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3408 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3409 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3410
3411 /* The failure may occur w/o any error info (from RPC), so provide one */
3412 if (FAILED(rc))
3413 setError(VBOX_E_VM_ERROR,
3414 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3415
3416 if ( SUCCEEDED(rc)
3417 && fLaunchingVMProcess
3418 )
3419 {
3420 /* complete the remote session initialization */
3421
3422 /* get the console from the direct session */
3423 ComPtr<IConsole> console;
3424 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3425 ComAssertComRC(rc);
3426
3427 if (SUCCEEDED(rc) && !console)
3428 {
3429 ComAssert(!!console);
3430 rc = E_FAIL;
3431 }
3432
3433 /* assign machine & console to the remote session */
3434 if (SUCCEEDED(rc))
3435 {
3436 /*
3437 * after LaunchVMProcess(), the first and the only
3438 * entry in remoteControls is that remote session
3439 */
3440 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3441 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3442 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3443
3444 /* The failure may occur w/o any error info (from RPC), so provide one */
3445 if (FAILED(rc))
3446 setError(VBOX_E_VM_ERROR,
3447 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3448 }
3449
3450 if (FAILED(rc))
3451 pSessionControl->Uninitialize();
3452 }
3453
3454 /* acquire the lock again */
3455 alock.acquire();
3456
3457 /* Restore the session state */
3458 mData->mSession.mState = origState;
3459 }
3460
3461 // finalize spawning anyway (this is why we don't return on errors above)
3462 if (fLaunchingVMProcess)
3463 {
3464 /* Note that the progress object is finalized later */
3465 /** @todo Consider checking mData->mSession.mProgress for cancellation
3466 * around here. */
3467
3468 /* We don't reset mSession.mPID here because it is necessary for
3469 * SessionMachine::uninit() to reap the child process later. */
3470
3471 if (FAILED(rc))
3472 {
3473 /* Close the remote session, remove the remote control from the list
3474 * and reset session state to Closed (@note keep the code in sync
3475 * with the relevant part in openSession()). */
3476
3477 Assert(mData->mSession.mRemoteControls.size() == 1);
3478 if (mData->mSession.mRemoteControls.size() == 1)
3479 {
3480 ErrorInfoKeeper eik;
3481 mData->mSession.mRemoteControls.front()->Uninitialize();
3482 }
3483
3484 mData->mSession.mRemoteControls.clear();
3485 mData->mSession.mState = SessionState_Unlocked;
3486 }
3487 }
3488 else
3489 {
3490 /* memorize PID of the directly opened session */
3491 if (SUCCEEDED(rc))
3492 mData->mSession.mPID = pid;
3493 }
3494
3495 if (SUCCEEDED(rc))
3496 {
3497 /* memorize the direct session control and cache IUnknown for it */
3498 mData->mSession.mDirectControl = pSessionControl;
3499 mData->mSession.mState = SessionState_Locked;
3500 /* associate the SessionMachine with this Machine */
3501 mData->mSession.mMachine = sessionMachine;
3502
3503 /* request an IUnknown pointer early from the remote party for later
3504 * identity checks (it will be internally cached within mDirectControl
3505 * at least on XPCOM) */
3506 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3507 NOREF(unk);
3508 }
3509
3510 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3511 * would break the lock order */
3512 alock.release();
3513
3514 /* uninitialize the created session machine on failure */
3515 if (FAILED(rc))
3516 sessionMachine->uninit();
3517
3518 }
3519
3520 if (SUCCEEDED(rc))
3521 {
3522 /*
3523 * tell the client watcher thread to update the set of
3524 * machines that have open sessions
3525 */
3526 mParent->updateClientWatcher();
3527
3528 if (oldState != SessionState_Locked)
3529 /* fire an event */
3530 mParent->onSessionStateChange(getId(), SessionState_Locked);
3531 }
3532
3533 return rc;
3534}
3535
3536/**
3537 * @note Locks objects!
3538 */
3539STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3540 IN_BSTR aType,
3541 IN_BSTR aEnvironment,
3542 IProgress **aProgress)
3543{
3544 CheckComArgStrNotEmptyOrNull(aType);
3545 Utf8Str strType(aType);
3546 Utf8Str strEnvironment(aEnvironment);
3547 /* "emergencystop" doesn't need the session, so skip the checks/interface
3548 * retrieval. This code doesn't quite fit in here, but introducing a
3549 * special API method would be even more effort, and would require explicit
3550 * support by every API client. It's better to hide the feature a bit. */
3551 if (strType != "emergencystop")
3552 CheckComArgNotNull(aSession);
3553 CheckComArgOutPointerValid(aProgress);
3554
3555 AutoCaller autoCaller(this);
3556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3557
3558 ComPtr<IInternalSessionControl> control;
3559 HRESULT rc = S_OK;
3560
3561 if (strType != "emergencystop")
3562 {
3563 /* check the session state */
3564 SessionState_T state;
3565 rc = aSession->COMGETTER(State)(&state);
3566 if (FAILED(rc))
3567 return rc;
3568
3569 if (state != SessionState_Unlocked)
3570 return setError(VBOX_E_INVALID_OBJECT_STATE,
3571 tr("The given session is busy"));
3572
3573 /* get the IInternalSessionControl interface */
3574 control = aSession;
3575 ComAssertMsgRet(!control.isNull(),
3576 ("No IInternalSessionControl interface"),
3577 E_INVALIDARG);
3578 }
3579
3580 /* get the teleporter enable state for the progress object init. */
3581 BOOL fTeleporterEnabled;
3582 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3583 if (FAILED(rc))
3584 return rc;
3585
3586 /* create a progress object */
3587 if (strType != "emergencystop")
3588 {
3589 ComObjPtr<ProgressProxy> progress;
3590 progress.createObject();
3591 rc = progress->init(mParent,
3592 static_cast<IMachine*>(this),
3593 Bstr(tr("Starting VM")).raw(),
3594 TRUE /* aCancelable */,
3595 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3596 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3597 2 /* uFirstOperationWeight */,
3598 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3599
3600 if (SUCCEEDED(rc))
3601 {
3602 rc = launchVMProcess(control, strType, strEnvironment, progress);
3603 if (SUCCEEDED(rc))
3604 {
3605 progress.queryInterfaceTo(aProgress);
3606
3607 /* signal the client watcher thread */
3608 mParent->updateClientWatcher();
3609
3610 /* fire an event */
3611 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3612 }
3613 }
3614 }
3615 else
3616 {
3617 /* no progress object - either instant success or failure */
3618 *aProgress = NULL;
3619
3620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3621
3622 if (mData->mSession.mState != SessionState_Locked)
3623 return setError(VBOX_E_INVALID_OBJECT_STATE,
3624 tr("The machine '%s' is not locked by a session"),
3625 mUserData->s.strName.c_str());
3626
3627 /* must have a VM process associated - do not kill normal API clients
3628 * with an open session */
3629 if (!Global::IsOnline(mData->mMachineState))
3630 return setError(VBOX_E_INVALID_OBJECT_STATE,
3631 tr("The machine '%s' does not have a VM process"),
3632 mUserData->s.strName.c_str());
3633
3634 /* forcibly terminate the VM process */
3635 if (mData->mSession.mPID != NIL_RTPROCESS)
3636 RTProcTerminate(mData->mSession.mPID);
3637
3638 /* signal the client watcher thread, as most likely the client has
3639 * been terminated */
3640 mParent->updateClientWatcher();
3641 }
3642
3643 return rc;
3644}
3645
3646STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3647{
3648 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3649 return setError(E_INVALIDARG,
3650 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3651 aPosition, SchemaDefs::MaxBootPosition);
3652
3653 if (aDevice == DeviceType_USB)
3654 return setError(E_NOTIMPL,
3655 tr("Booting from USB device is currently not supported"));
3656
3657 AutoCaller autoCaller(this);
3658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3659
3660 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3661
3662 HRESULT rc = checkStateDependency(MutableStateDep);
3663 if (FAILED(rc)) return rc;
3664
3665 setModified(IsModified_MachineData);
3666 mHWData.backup();
3667 mHWData->mBootOrder[aPosition - 1] = aDevice;
3668
3669 return S_OK;
3670}
3671
3672STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3673{
3674 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3675 return setError(E_INVALIDARG,
3676 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3677 aPosition, SchemaDefs::MaxBootPosition);
3678
3679 AutoCaller autoCaller(this);
3680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3681
3682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3683
3684 *aDevice = mHWData->mBootOrder[aPosition - 1];
3685
3686 return S_OK;
3687}
3688
3689STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3690 LONG aControllerPort,
3691 LONG aDevice,
3692 DeviceType_T aType,
3693 IMedium *aMedium)
3694{
3695 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3696 aControllerName, aControllerPort, aDevice, aType, aMedium));
3697
3698 CheckComArgStrNotEmptyOrNull(aControllerName);
3699
3700 AutoCaller autoCaller(this);
3701 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3702
3703 // request the host lock first, since might be calling Host methods for getting host drives;
3704 // next, protect the media tree all the while we're in here, as well as our member variables
3705 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3706 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3707
3708 HRESULT rc = checkStateDependency(MutableStateDep);
3709 if (FAILED(rc)) return rc;
3710
3711 /// @todo NEWMEDIA implicit machine registration
3712 if (!mData->mRegistered)
3713 return setError(VBOX_E_INVALID_OBJECT_STATE,
3714 tr("Cannot attach storage devices to an unregistered machine"));
3715
3716 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3717
3718 /* Check for an existing controller. */
3719 ComObjPtr<StorageController> ctl;
3720 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3721 if (FAILED(rc)) return rc;
3722
3723 StorageControllerType_T ctrlType;
3724 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3725 if (FAILED(rc))
3726 return setError(E_FAIL,
3727 tr("Could not get type of controller '%ls'"),
3728 aControllerName);
3729
3730 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3731 bool fHotplug = false;
3732 if (Global::IsOnlineOrTransient(mData->mMachineState))
3733 fHotplug = true;
3734
3735 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3736 return setError(VBOX_E_INVALID_VM_STATE,
3737 tr("Controller '%ls' does not support hotplugging"),
3738 aControllerName);
3739
3740 // check that the port and device are not out of range
3741 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3742 if (FAILED(rc)) return rc;
3743
3744 /* check if the device slot is already busy */
3745 MediumAttachment *pAttachTemp;
3746 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3747 aControllerName,
3748 aControllerPort,
3749 aDevice)))
3750 {
3751 Medium *pMedium = pAttachTemp->getMedium();
3752 if (pMedium)
3753 {
3754 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3755 return setError(VBOX_E_OBJECT_IN_USE,
3756 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3757 pMedium->getLocationFull().c_str(),
3758 aControllerPort,
3759 aDevice,
3760 aControllerName);
3761 }
3762 else
3763 return setError(VBOX_E_OBJECT_IN_USE,
3764 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3765 aControllerPort, aDevice, aControllerName);
3766 }
3767
3768 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3769 if (aMedium && medium.isNull())
3770 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3771
3772 AutoCaller mediumCaller(medium);
3773 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3774
3775 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3776
3777 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3778 && !medium.isNull()
3779 )
3780 return setError(VBOX_E_OBJECT_IN_USE,
3781 tr("Medium '%s' is already attached to this virtual machine"),
3782 medium->getLocationFull().c_str());
3783
3784 if (!medium.isNull())
3785 {
3786 MediumType_T mtype = medium->getType();
3787 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3788 // For DVDs it's not written to the config file, so needs no global config
3789 // version bump. For floppies it's a new attribute "type", which is ignored
3790 // by older VirtualBox version, so needs no global config version bump either.
3791 // For hard disks this type is not accepted.
3792 if (mtype == MediumType_MultiAttach)
3793 {
3794 // This type is new with VirtualBox 4.0 and therefore requires settings
3795 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3796 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3797 // two reasons: The medium type is a property of the media registry tree, which
3798 // can reside in the global config file (for pre-4.0 media); we would therefore
3799 // possibly need to bump the global config version. We don't want to do that though
3800 // because that might make downgrading to pre-4.0 impossible.
3801 // As a result, we can only use these two new types if the medium is NOT in the
3802 // global registry:
3803 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3804 if ( medium->isInRegistry(uuidGlobalRegistry)
3805 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3806 )
3807 return setError(VBOX_E_INVALID_OBJECT_STATE,
3808 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3809 "to machines that were created with VirtualBox 4.0 or later"),
3810 medium->getLocationFull().c_str());
3811 }
3812 }
3813
3814 bool fIndirect = false;
3815 if (!medium.isNull())
3816 fIndirect = medium->isReadOnly();
3817 bool associate = true;
3818
3819 do
3820 {
3821 if ( aType == DeviceType_HardDisk
3822 && mMediaData.isBackedUp())
3823 {
3824 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3825
3826 /* check if the medium was attached to the VM before we started
3827 * changing attachments in which case the attachment just needs to
3828 * be restored */
3829 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3830 {
3831 AssertReturn(!fIndirect, E_FAIL);
3832
3833 /* see if it's the same bus/channel/device */
3834 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3835 {
3836 /* the simplest case: restore the whole attachment
3837 * and return, nothing else to do */
3838 mMediaData->mAttachments.push_back(pAttachTemp);
3839 return S_OK;
3840 }
3841
3842 /* bus/channel/device differ; we need a new attachment object,
3843 * but don't try to associate it again */
3844 associate = false;
3845 break;
3846 }
3847 }
3848
3849 /* go further only if the attachment is to be indirect */
3850 if (!fIndirect)
3851 break;
3852
3853 /* perform the so called smart attachment logic for indirect
3854 * attachments. Note that smart attachment is only applicable to base
3855 * hard disks. */
3856
3857 if (medium->getParent().isNull())
3858 {
3859 /* first, investigate the backup copy of the current hard disk
3860 * attachments to make it possible to re-attach existing diffs to
3861 * another device slot w/o losing their contents */
3862 if (mMediaData.isBackedUp())
3863 {
3864 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3865
3866 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3867 uint32_t foundLevel = 0;
3868
3869 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3870 it != oldAtts.end();
3871 ++it)
3872 {
3873 uint32_t level = 0;
3874 MediumAttachment *pAttach = *it;
3875 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3876 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3877 if (pMedium.isNull())
3878 continue;
3879
3880 if (pMedium->getBase(&level) == medium)
3881 {
3882 /* skip the hard disk if its currently attached (we
3883 * cannot attach the same hard disk twice) */
3884 if (findAttachment(mMediaData->mAttachments,
3885 pMedium))
3886 continue;
3887
3888 /* matched device, channel and bus (i.e. attached to the
3889 * same place) will win and immediately stop the search;
3890 * otherwise the attachment that has the youngest
3891 * descendant of medium will be used
3892 */
3893 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3894 {
3895 /* the simplest case: restore the whole attachment
3896 * and return, nothing else to do */
3897 mMediaData->mAttachments.push_back(*it);
3898 return S_OK;
3899 }
3900 else if ( foundIt == oldAtts.end()
3901 || level > foundLevel /* prefer younger */
3902 )
3903 {
3904 foundIt = it;
3905 foundLevel = level;
3906 }
3907 }
3908 }
3909
3910 if (foundIt != oldAtts.end())
3911 {
3912 /* use the previously attached hard disk */
3913 medium = (*foundIt)->getMedium();
3914 mediumCaller.attach(medium);
3915 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3916 mediumLock.attach(medium);
3917 /* not implicit, doesn't require association with this VM */
3918 fIndirect = false;
3919 associate = false;
3920 /* go right to the MediumAttachment creation */
3921 break;
3922 }
3923 }
3924
3925 /* must give up the medium lock and medium tree lock as below we
3926 * go over snapshots, which needs a lock with higher lock order. */
3927 mediumLock.release();
3928 treeLock.release();
3929
3930 /* then, search through snapshots for the best diff in the given
3931 * hard disk's chain to base the new diff on */
3932
3933 ComObjPtr<Medium> base;
3934 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3935 while (snap)
3936 {
3937 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3938
3939 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3940
3941 MediumAttachment *pAttachFound = NULL;
3942 uint32_t foundLevel = 0;
3943
3944 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3945 it != snapAtts.end();
3946 ++it)
3947 {
3948 MediumAttachment *pAttach = *it;
3949 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3950 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3951 if (pMedium.isNull())
3952 continue;
3953
3954 uint32_t level = 0;
3955 if (pMedium->getBase(&level) == medium)
3956 {
3957 /* matched device, channel and bus (i.e. attached to the
3958 * same place) will win and immediately stop the search;
3959 * otherwise the attachment that has the youngest
3960 * descendant of medium will be used
3961 */
3962 if ( pAttach->getDevice() == aDevice
3963 && pAttach->getPort() == aControllerPort
3964 && pAttach->getControllerName() == aControllerName
3965 )
3966 {
3967 pAttachFound = pAttach;
3968 break;
3969 }
3970 else if ( !pAttachFound
3971 || level > foundLevel /* prefer younger */
3972 )
3973 {
3974 pAttachFound = pAttach;
3975 foundLevel = level;
3976 }
3977 }
3978 }
3979
3980 if (pAttachFound)
3981 {
3982 base = pAttachFound->getMedium();
3983 break;
3984 }
3985
3986 snap = snap->getParent();
3987 }
3988
3989 /* re-lock medium tree and the medium, as we need it below */
3990 treeLock.acquire();
3991 mediumLock.acquire();
3992
3993 /* found a suitable diff, use it as a base */
3994 if (!base.isNull())
3995 {
3996 medium = base;
3997 mediumCaller.attach(medium);
3998 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3999 mediumLock.attach(medium);
4000 }
4001 }
4002
4003 Utf8Str strFullSnapshotFolder;
4004 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4005
4006 ComObjPtr<Medium> diff;
4007 diff.createObject();
4008 // store this diff in the same registry as the parent
4009 Guid uuidRegistryParent;
4010 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4011 {
4012 // parent image has no registry: this can happen if we're attaching a new immutable
4013 // image that has not yet been attached (medium then points to the base and we're
4014 // creating the diff image for the immutable, and the parent is not yet registered);
4015 // put the parent in the machine registry then
4016 mediumLock.release();
4017 treeLock.release();
4018 alock.release();
4019 addMediumToRegistry(medium);
4020 alock.acquire();
4021 treeLock.acquire();
4022 mediumLock.acquire();
4023 medium->getFirstRegistryMachineId(uuidRegistryParent);
4024 }
4025 rc = diff->init(mParent,
4026 medium->getPreferredDiffFormat(),
4027 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4028 uuidRegistryParent);
4029 if (FAILED(rc)) return rc;
4030
4031 /* Apply the normal locking logic to the entire chain. */
4032 MediumLockList *pMediumLockList(new MediumLockList());
4033 mediumLock.release();
4034 treeLock.release();
4035 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4036 true /* fMediumLockWrite */,
4037 medium,
4038 *pMediumLockList);
4039 treeLock.acquire();
4040 mediumLock.acquire();
4041 if (SUCCEEDED(rc))
4042 {
4043 mediumLock.release();
4044 treeLock.release();
4045 rc = pMediumLockList->Lock();
4046 treeLock.acquire();
4047 mediumLock.acquire();
4048 if (FAILED(rc))
4049 setError(rc,
4050 tr("Could not lock medium when creating diff '%s'"),
4051 diff->getLocationFull().c_str());
4052 else
4053 {
4054 /* will release the lock before the potentially lengthy
4055 * operation, so protect with the special state */
4056 MachineState_T oldState = mData->mMachineState;
4057 setMachineState(MachineState_SettingUp);
4058
4059 mediumLock.release();
4060 treeLock.release();
4061 alock.release();
4062
4063 rc = medium->createDiffStorage(diff,
4064 MediumVariant_Standard,
4065 pMediumLockList,
4066 NULL /* aProgress */,
4067 true /* aWait */);
4068
4069 alock.acquire();
4070 treeLock.acquire();
4071 mediumLock.acquire();
4072
4073 setMachineState(oldState);
4074 }
4075 }
4076
4077 /* Unlock the media and free the associated memory. */
4078 delete pMediumLockList;
4079
4080 if (FAILED(rc)) return rc;
4081
4082 /* use the created diff for the actual attachment */
4083 medium = diff;
4084 mediumCaller.attach(medium);
4085 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4086 mediumLock.attach(medium);
4087 }
4088 while (0);
4089
4090 ComObjPtr<MediumAttachment> attachment;
4091 attachment.createObject();
4092 rc = attachment->init(this,
4093 medium,
4094 aControllerName,
4095 aControllerPort,
4096 aDevice,
4097 aType,
4098 fIndirect,
4099 false /* fPassthrough */,
4100 false /* fTempEject */,
4101 false /* fNonRotational */,
4102 false /* fDiscard */,
4103 Utf8Str::Empty);
4104 if (FAILED(rc)) return rc;
4105
4106 if (associate && !medium.isNull())
4107 {
4108 // as the last step, associate the medium to the VM
4109 rc = medium->addBackReference(mData->mUuid);
4110 // here we can fail because of Deleting, or being in process of creating a Diff
4111 if (FAILED(rc)) return rc;
4112
4113 mediumLock.release();
4114 treeLock.release();
4115 alock.release();
4116 addMediumToRegistry(medium);
4117 alock.acquire();
4118 treeLock.acquire();
4119 mediumLock.acquire();
4120 }
4121
4122 /* success: finally remember the attachment */
4123 setModified(IsModified_Storage);
4124 mMediaData.backup();
4125 mMediaData->mAttachments.push_back(attachment);
4126
4127 mediumLock.release();
4128 treeLock.release();
4129 alock.release();
4130
4131 if (fHotplug)
4132 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4133
4134 mParent->saveModifiedRegistries();
4135
4136 return rc;
4137}
4138
4139STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4140 LONG aDevice)
4141{
4142 CheckComArgStrNotEmptyOrNull(aControllerName);
4143
4144 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4145 aControllerName, aControllerPort, aDevice));
4146
4147 AutoCaller autoCaller(this);
4148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4149
4150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4151
4152 HRESULT rc = checkStateDependency(MutableStateDep);
4153 if (FAILED(rc)) return rc;
4154
4155 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4156
4157 /* Check for an existing controller. */
4158 ComObjPtr<StorageController> ctl;
4159 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4160 if (FAILED(rc)) return rc;
4161
4162 StorageControllerType_T ctrlType;
4163 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4164 if (FAILED(rc))
4165 return setError(E_FAIL,
4166 tr("Could not get type of controller '%ls'"),
4167 aControllerName);
4168
4169 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4170 bool fHotplug = false;
4171 if (Global::IsOnlineOrTransient(mData->mMachineState))
4172 fHotplug = true;
4173
4174 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4175 return setError(VBOX_E_INVALID_VM_STATE,
4176 tr("Controller '%ls' does not support hotplugging"),
4177 aControllerName);
4178
4179 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4180 aControllerName,
4181 aControllerPort,
4182 aDevice);
4183 if (!pAttach)
4184 return setError(VBOX_E_OBJECT_NOT_FOUND,
4185 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4186 aDevice, aControllerPort, aControllerName);
4187
4188 /*
4189 * The VM has to detach the device before we delete any implicit diffs.
4190 * If this fails we can roll back without loosing data.
4191 */
4192 if (fHotplug)
4193 {
4194 alock.release();
4195 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4196 alock.acquire();
4197 }
4198 if (FAILED(rc)) return rc;
4199
4200 /* If we are here everything went well and we can delete the implicit now. */
4201 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4202
4203 alock.release();
4204
4205 mParent->saveModifiedRegistries();
4206
4207 return rc;
4208}
4209
4210STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4211 LONG aDevice, BOOL aPassthrough)
4212{
4213 CheckComArgStrNotEmptyOrNull(aControllerName);
4214
4215 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4216 aControllerName, aControllerPort, aDevice, aPassthrough));
4217
4218 AutoCaller autoCaller(this);
4219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4220
4221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4222
4223 HRESULT rc = checkStateDependency(MutableStateDep);
4224 if (FAILED(rc)) return rc;
4225
4226 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4227
4228 if (Global::IsOnlineOrTransient(mData->mMachineState))
4229 return setError(VBOX_E_INVALID_VM_STATE,
4230 tr("Invalid machine state: %s"),
4231 Global::stringifyMachineState(mData->mMachineState));
4232
4233 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4234 aControllerName,
4235 aControllerPort,
4236 aDevice);
4237 if (!pAttach)
4238 return setError(VBOX_E_OBJECT_NOT_FOUND,
4239 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4240 aDevice, aControllerPort, aControllerName);
4241
4242
4243 setModified(IsModified_Storage);
4244 mMediaData.backup();
4245
4246 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4247
4248 if (pAttach->getType() != DeviceType_DVD)
4249 return setError(E_INVALIDARG,
4250 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4251 aDevice, aControllerPort, aControllerName);
4252 pAttach->updatePassthrough(!!aPassthrough);
4253
4254 return S_OK;
4255}
4256
4257STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4258 LONG aDevice, BOOL aTemporaryEject)
4259{
4260 CheckComArgStrNotEmptyOrNull(aControllerName);
4261
4262 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4263 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4264
4265 AutoCaller autoCaller(this);
4266 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4267
4268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4269
4270 HRESULT rc = checkStateDependency(MutableStateDep);
4271 if (FAILED(rc)) return rc;
4272
4273 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4274 aControllerName,
4275 aControllerPort,
4276 aDevice);
4277 if (!pAttach)
4278 return setError(VBOX_E_OBJECT_NOT_FOUND,
4279 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4280 aDevice, aControllerPort, aControllerName);
4281
4282
4283 setModified(IsModified_Storage);
4284 mMediaData.backup();
4285
4286 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4287
4288 if (pAttach->getType() != DeviceType_DVD)
4289 return setError(E_INVALIDARG,
4290 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4291 aDevice, aControllerPort, aControllerName);
4292 pAttach->updateTempEject(!!aTemporaryEject);
4293
4294 return S_OK;
4295}
4296
4297STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4298 LONG aDevice, BOOL aNonRotational)
4299{
4300 CheckComArgStrNotEmptyOrNull(aControllerName);
4301
4302 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4303 aControllerName, aControllerPort, aDevice, aNonRotational));
4304
4305 AutoCaller autoCaller(this);
4306 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4307
4308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4309
4310 HRESULT rc = checkStateDependency(MutableStateDep);
4311 if (FAILED(rc)) return rc;
4312
4313 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4314
4315 if (Global::IsOnlineOrTransient(mData->mMachineState))
4316 return setError(VBOX_E_INVALID_VM_STATE,
4317 tr("Invalid machine state: %s"),
4318 Global::stringifyMachineState(mData->mMachineState));
4319
4320 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4321 aControllerName,
4322 aControllerPort,
4323 aDevice);
4324 if (!pAttach)
4325 return setError(VBOX_E_OBJECT_NOT_FOUND,
4326 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4327 aDevice, aControllerPort, aControllerName);
4328
4329
4330 setModified(IsModified_Storage);
4331 mMediaData.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->getType() != DeviceType_HardDisk)
4336 return setError(E_INVALIDARG,
4337 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"),
4338 aDevice, aControllerPort, aControllerName);
4339 pAttach->updateNonRotational(!!aNonRotational);
4340
4341 return S_OK;
4342}
4343
4344STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4345 LONG aDevice, BOOL aDiscard)
4346{
4347 CheckComArgStrNotEmptyOrNull(aControllerName);
4348
4349 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4350 aControllerName, aControllerPort, aDevice, aDiscard));
4351
4352 AutoCaller autoCaller(this);
4353 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4354
4355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4356
4357 HRESULT rc = checkStateDependency(MutableStateDep);
4358 if (FAILED(rc)) return rc;
4359
4360 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4361
4362 if (Global::IsOnlineOrTransient(mData->mMachineState))
4363 return setError(VBOX_E_INVALID_VM_STATE,
4364 tr("Invalid machine state: %s"),
4365 Global::stringifyMachineState(mData->mMachineState));
4366
4367 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4368 aControllerName,
4369 aControllerPort,
4370 aDevice);
4371 if (!pAttach)
4372 return setError(VBOX_E_OBJECT_NOT_FOUND,
4373 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4374 aDevice, aControllerPort, aControllerName);
4375
4376
4377 setModified(IsModified_Storage);
4378 mMediaData.backup();
4379
4380 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4381
4382 if (pAttach->getType() != DeviceType_HardDisk)
4383 return setError(E_INVALIDARG,
4384 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"),
4385 aDevice, aControllerPort, aControllerName);
4386 pAttach->updateDiscard(!!aDiscard);
4387
4388 return S_OK;
4389}
4390
4391STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4392 LONG aDevice)
4393{
4394 int rc = S_OK;
4395 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4396 aControllerName, aControllerPort, aDevice));
4397
4398 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4399
4400 return rc;
4401}
4402
4403STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4404 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4405{
4406 CheckComArgStrNotEmptyOrNull(aControllerName);
4407
4408 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4409 aControllerName, aControllerPort, aDevice));
4410
4411 AutoCaller autoCaller(this);
4412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4413
4414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4415
4416 HRESULT rc = checkStateDependency(MutableStateDep);
4417 if (FAILED(rc)) return rc;
4418
4419 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4420
4421 if (Global::IsOnlineOrTransient(mData->mMachineState))
4422 return setError(VBOX_E_INVALID_VM_STATE,
4423 tr("Invalid machine state: %s"),
4424 Global::stringifyMachineState(mData->mMachineState));
4425
4426 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4427 aControllerName,
4428 aControllerPort,
4429 aDevice);
4430 if (!pAttach)
4431 return setError(VBOX_E_OBJECT_NOT_FOUND,
4432 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4433 aDevice, aControllerPort, aControllerName);
4434
4435
4436 setModified(IsModified_Storage);
4437 mMediaData.backup();
4438
4439 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4440 if (aBandwidthGroup && group.isNull())
4441 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4442
4443 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4444
4445 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4446 if (strBandwidthGroupOld.isNotEmpty())
4447 {
4448 /* Get the bandwidth group object and release it - this must not fail. */
4449 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4450 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4451 Assert(SUCCEEDED(rc));
4452
4453 pBandwidthGroupOld->release();
4454 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4455 }
4456
4457 if (!group.isNull())
4458 {
4459 group->reference();
4460 pAttach->updateBandwidthGroup(group->getName());
4461 }
4462
4463 return S_OK;
4464}
4465
4466STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4467 LONG aControllerPort,
4468 LONG aDevice,
4469 DeviceType_T aType)
4470{
4471 HRESULT rc = S_OK;
4472
4473 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4474 aControllerName, aControllerPort, aDevice, aType));
4475
4476 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4477
4478 return rc;
4479}
4480
4481
4482
4483STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4484 LONG aControllerPort,
4485 LONG aDevice,
4486 BOOL aForce)
4487{
4488 int rc = S_OK;
4489 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4490 aControllerName, aControllerPort, aForce));
4491
4492 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4493
4494 return rc;
4495}
4496
4497STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4498 LONG aControllerPort,
4499 LONG aDevice,
4500 IMedium *aMedium,
4501 BOOL aForce)
4502{
4503 int rc = S_OK;
4504 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4505 aControllerName, aControllerPort, aDevice, aForce));
4506
4507 CheckComArgStrNotEmptyOrNull(aControllerName);
4508
4509 AutoCaller autoCaller(this);
4510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4511
4512 // request the host lock first, since might be calling Host methods for getting host drives;
4513 // next, protect the media tree all the while we're in here, as well as our member variables
4514 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4515 this->lockHandle(),
4516 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4517
4518 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4519 aControllerName,
4520 aControllerPort,
4521 aDevice);
4522 if (pAttach.isNull())
4523 return setError(VBOX_E_OBJECT_NOT_FOUND,
4524 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4525 aDevice, aControllerPort, aControllerName);
4526
4527 /* Remember previously mounted medium. The medium before taking the
4528 * backup is not necessarily the same thing. */
4529 ComObjPtr<Medium> oldmedium;
4530 oldmedium = pAttach->getMedium();
4531
4532 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4533 if (aMedium && pMedium.isNull())
4534 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4535
4536 AutoCaller mediumCaller(pMedium);
4537 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4538
4539 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4540 if (pMedium)
4541 {
4542 DeviceType_T mediumType = pAttach->getType();
4543 switch (mediumType)
4544 {
4545 case DeviceType_DVD:
4546 case DeviceType_Floppy:
4547 break;
4548
4549 default:
4550 return setError(VBOX_E_INVALID_OBJECT_STATE,
4551 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4552 aControllerPort,
4553 aDevice,
4554 aControllerName);
4555 }
4556 }
4557
4558 setModified(IsModified_Storage);
4559 mMediaData.backup();
4560
4561 {
4562 // The backup operation makes the pAttach reference point to the
4563 // old settings. Re-get the correct reference.
4564 pAttach = findAttachment(mMediaData->mAttachments,
4565 aControllerName,
4566 aControllerPort,
4567 aDevice);
4568 if (!oldmedium.isNull())
4569 oldmedium->removeBackReference(mData->mUuid);
4570 if (!pMedium.isNull())
4571 {
4572 pMedium->addBackReference(mData->mUuid);
4573
4574 mediumLock.release();
4575 multiLock.release();
4576 addMediumToRegistry(pMedium);
4577 multiLock.acquire();
4578 mediumLock.acquire();
4579 }
4580
4581 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4582 pAttach->updateMedium(pMedium);
4583 }
4584
4585 setModified(IsModified_Storage);
4586
4587 mediumLock.release();
4588 multiLock.release();
4589 rc = onMediumChange(pAttach, aForce);
4590 multiLock.acquire();
4591 mediumLock.acquire();
4592
4593 /* On error roll back this change only. */
4594 if (FAILED(rc))
4595 {
4596 if (!pMedium.isNull())
4597 pMedium->removeBackReference(mData->mUuid);
4598 pAttach = findAttachment(mMediaData->mAttachments,
4599 aControllerName,
4600 aControllerPort,
4601 aDevice);
4602 /* If the attachment is gone in the meantime, bail out. */
4603 if (pAttach.isNull())
4604 return rc;
4605 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4606 if (!oldmedium.isNull())
4607 oldmedium->addBackReference(mData->mUuid);
4608 pAttach->updateMedium(oldmedium);
4609 }
4610
4611 mediumLock.release();
4612 multiLock.release();
4613
4614 mParent->saveModifiedRegistries();
4615
4616 return rc;
4617}
4618
4619STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4620 LONG aControllerPort,
4621 LONG aDevice,
4622 IMedium **aMedium)
4623{
4624 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4625 aControllerName, aControllerPort, aDevice));
4626
4627 CheckComArgStrNotEmptyOrNull(aControllerName);
4628 CheckComArgOutPointerValid(aMedium);
4629
4630 AutoCaller autoCaller(this);
4631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4632
4633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4634
4635 *aMedium = NULL;
4636
4637 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4638 aControllerName,
4639 aControllerPort,
4640 aDevice);
4641 if (pAttach.isNull())
4642 return setError(VBOX_E_OBJECT_NOT_FOUND,
4643 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4644 aDevice, aControllerPort, aControllerName);
4645
4646 pAttach->getMedium().queryInterfaceTo(aMedium);
4647
4648 return S_OK;
4649}
4650
4651STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4652{
4653 CheckComArgOutPointerValid(port);
4654 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4655
4656 AutoCaller autoCaller(this);
4657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4658
4659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4660
4661 mSerialPorts[slot].queryInterfaceTo(port);
4662
4663 return S_OK;
4664}
4665
4666STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4667{
4668 CheckComArgOutPointerValid(port);
4669 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4670
4671 AutoCaller autoCaller(this);
4672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4673
4674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4675
4676 mParallelPorts[slot].queryInterfaceTo(port);
4677
4678 return S_OK;
4679}
4680
4681STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4682{
4683 CheckComArgOutPointerValid(adapter);
4684 /* Do not assert if slot is out of range, just return the advertised
4685 status. testdriver/vbox.py triggers this in logVmInfo. */
4686 if (slot >= mNetworkAdapters.size())
4687 return setError(E_INVALIDARG,
4688 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4689 slot, mNetworkAdapters.size());
4690
4691 AutoCaller autoCaller(this);
4692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4693
4694 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4695
4696 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4697
4698 return S_OK;
4699}
4700
4701STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4702{
4703 CheckComArgOutSafeArrayPointerValid(aKeys);
4704
4705 AutoCaller autoCaller(this);
4706 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4707
4708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4709
4710 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4711 int i = 0;
4712 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4713 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4714 ++it, ++i)
4715 {
4716 const Utf8Str &strKey = it->first;
4717 strKey.cloneTo(&saKeys[i]);
4718 }
4719 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4720
4721 return S_OK;
4722 }
4723
4724 /**
4725 * @note Locks this object for reading.
4726 */
4727STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4728 BSTR *aValue)
4729{
4730 CheckComArgStrNotEmptyOrNull(aKey);
4731 CheckComArgOutPointerValid(aValue);
4732
4733 AutoCaller autoCaller(this);
4734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4735
4736 /* start with nothing found */
4737 Bstr bstrResult("");
4738
4739 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4740
4741 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4742 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4743 // found:
4744 bstrResult = it->second; // source is a Utf8Str
4745
4746 /* return the result to caller (may be empty) */
4747 bstrResult.cloneTo(aValue);
4748
4749 return S_OK;
4750}
4751
4752 /**
4753 * @note Locks mParent for writing + this object for writing.
4754 */
4755STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4756{
4757 CheckComArgStrNotEmptyOrNull(aKey);
4758
4759 AutoCaller autoCaller(this);
4760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4761
4762 Utf8Str strKey(aKey);
4763 Utf8Str strValue(aValue);
4764 Utf8Str strOldValue; // empty
4765
4766 // locking note: we only hold the read lock briefly to look up the old value,
4767 // then release it and call the onExtraCanChange callbacks. There is a small
4768 // chance of a race insofar as the callback might be called twice if two callers
4769 // change the same key at the same time, but that's a much better solution
4770 // than the deadlock we had here before. The actual changing of the extradata
4771 // is then performed under the write lock and race-free.
4772
4773 // look up the old value first; if nothing has changed then we need not do anything
4774 {
4775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4776 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4777 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4778 strOldValue = it->second;
4779 }
4780
4781 bool fChanged;
4782 if ((fChanged = (strOldValue != strValue)))
4783 {
4784 // ask for permission from all listeners outside the locks;
4785 // onExtraDataCanChange() only briefly requests the VirtualBox
4786 // lock to copy the list of callbacks to invoke
4787 Bstr error;
4788 Bstr bstrValue(aValue);
4789
4790 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4791 {
4792 const char *sep = error.isEmpty() ? "" : ": ";
4793 CBSTR err = error.raw();
4794 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4795 sep, err));
4796 return setError(E_ACCESSDENIED,
4797 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4798 aKey,
4799 bstrValue.raw(),
4800 sep,
4801 err);
4802 }
4803
4804 // data is changing and change not vetoed: then write it out under the lock
4805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4806
4807 if (isSnapshotMachine())
4808 {
4809 HRESULT rc = checkStateDependency(MutableStateDep);
4810 if (FAILED(rc)) return rc;
4811 }
4812
4813 if (strValue.isEmpty())
4814 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4815 else
4816 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4817 // creates a new key if needed
4818
4819 bool fNeedsGlobalSaveSettings = false;
4820 saveSettings(&fNeedsGlobalSaveSettings);
4821
4822 if (fNeedsGlobalSaveSettings)
4823 {
4824 // save the global settings; for that we should hold only the VirtualBox lock
4825 alock.release();
4826 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4827 mParent->saveSettings();
4828 }
4829 }
4830
4831 // fire notification outside the lock
4832 if (fChanged)
4833 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4834
4835 return S_OK;
4836}
4837
4838STDMETHODIMP Machine::SaveSettings()
4839{
4840 AutoCaller autoCaller(this);
4841 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4842
4843 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4844
4845 /* when there was auto-conversion, we want to save the file even if
4846 * the VM is saved */
4847 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4848 if (FAILED(rc)) return rc;
4849
4850 /* the settings file path may never be null */
4851 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4852
4853 /* save all VM data excluding snapshots */
4854 bool fNeedsGlobalSaveSettings = false;
4855 rc = saveSettings(&fNeedsGlobalSaveSettings);
4856 mlock.release();
4857
4858 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4859 {
4860 // save the global settings; for that we should hold only the VirtualBox lock
4861 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4862 rc = mParent->saveSettings();
4863 }
4864
4865 return rc;
4866}
4867
4868STDMETHODIMP Machine::DiscardSettings()
4869{
4870 AutoCaller autoCaller(this);
4871 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4872
4873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4874
4875 HRESULT rc = checkStateDependency(MutableStateDep);
4876 if (FAILED(rc)) return rc;
4877
4878 /*
4879 * during this rollback, the session will be notified if data has
4880 * been actually changed
4881 */
4882 rollback(true /* aNotify */);
4883
4884 return S_OK;
4885}
4886
4887/** @note Locks objects! */
4888STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4889 ComSafeArrayOut(IMedium*, aMedia))
4890{
4891 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4892 AutoLimitedCaller autoCaller(this);
4893 AssertComRCReturnRC(autoCaller.rc());
4894
4895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4896
4897 Guid id(getId());
4898
4899 if (mData->mSession.mState != SessionState_Unlocked)
4900 return setError(VBOX_E_INVALID_OBJECT_STATE,
4901 tr("Cannot unregister the machine '%s' while it is locked"),
4902 mUserData->s.strName.c_str());
4903
4904 // wait for state dependents to drop to zero
4905 ensureNoStateDependencies();
4906
4907 if (!mData->mAccessible)
4908 {
4909 // inaccessible maschines can only be unregistered; uninitialize ourselves
4910 // here because currently there may be no unregistered that are inaccessible
4911 // (this state combination is not supported). Note releasing the caller and
4912 // leaving the lock before calling uninit()
4913 alock.release();
4914 autoCaller.release();
4915
4916 uninit();
4917
4918 mParent->unregisterMachine(this, id);
4919 // calls VirtualBox::saveSettings()
4920
4921 return S_OK;
4922 }
4923
4924 HRESULT rc = S_OK;
4925
4926 // discard saved state
4927 if (mData->mMachineState == MachineState_Saved)
4928 {
4929 // add the saved state file to the list of files the caller should delete
4930 Assert(!mSSData->strStateFilePath.isEmpty());
4931 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4932
4933 mSSData->strStateFilePath.setNull();
4934
4935 // unconditionally set the machine state to powered off, we now
4936 // know no session has locked the machine
4937 mData->mMachineState = MachineState_PoweredOff;
4938 }
4939
4940 size_t cSnapshots = 0;
4941 if (mData->mFirstSnapshot)
4942 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4943 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4944 // fail now before we start detaching media
4945 return setError(VBOX_E_INVALID_OBJECT_STATE,
4946 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4947 mUserData->s.strName.c_str(), cSnapshots);
4948
4949 // This list collects the medium objects from all medium attachments
4950 // which we will detach from the machine and its snapshots, in a specific
4951 // order which allows for closing all media without getting "media in use"
4952 // errors, simply by going through the list from the front to the back:
4953 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4954 // and must be closed before the parent media from the snapshots, or closing the parents
4955 // will fail because they still have children);
4956 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4957 // the root ("first") snapshot of the machine.
4958 MediaList llMedia;
4959
4960 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4961 && mMediaData->mAttachments.size()
4962 )
4963 {
4964 // we have media attachments: detach them all and add the Medium objects to our list
4965 if (cleanupMode != CleanupMode_UnregisterOnly)
4966 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4967 else
4968 return setError(VBOX_E_INVALID_OBJECT_STATE,
4969 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4970 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4971 }
4972
4973 if (cSnapshots)
4974 {
4975 // autoCleanup must be true here, or we would have failed above
4976
4977 // add the media from the medium attachments of the snapshots to llMedia
4978 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4979 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4980 // into the children first
4981
4982 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4983 MachineState_T oldState = mData->mMachineState;
4984 mData->mMachineState = MachineState_DeletingSnapshot;
4985
4986 // make a copy of the first snapshot so the refcount does not drop to 0
4987 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4988 // because of the AutoCaller voodoo)
4989 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4990
4991 // GO!
4992 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4993
4994 mData->mMachineState = oldState;
4995 }
4996
4997 if (FAILED(rc))
4998 {
4999 rollbackMedia();
5000 return rc;
5001 }
5002
5003 // commit all the media changes made above
5004 commitMedia();
5005
5006 mData->mRegistered = false;
5007
5008 // machine lock no longer needed
5009 alock.release();
5010
5011 // return media to caller
5012 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5013 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5014
5015 mParent->unregisterMachine(this, id);
5016 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5017
5018 return S_OK;
5019}
5020
5021struct Machine::DeleteTask
5022{
5023 ComObjPtr<Machine> pMachine;
5024 RTCList<ComPtr<IMedium> > llMediums;
5025 StringsList llFilesToDelete;
5026 ComObjPtr<Progress> pProgress;
5027};
5028
5029STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5030{
5031 LogFlowFuncEnter();
5032
5033 AutoCaller autoCaller(this);
5034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5035
5036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5037
5038 HRESULT rc = checkStateDependency(MutableStateDep);
5039 if (FAILED(rc)) return rc;
5040
5041 if (mData->mRegistered)
5042 return setError(VBOX_E_INVALID_VM_STATE,
5043 tr("Cannot delete settings of a registered machine"));
5044
5045 DeleteTask *pTask = new DeleteTask;
5046 pTask->pMachine = this;
5047 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5048
5049 // collect files to delete
5050 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5051
5052 for (size_t i = 0; i < sfaMedia.size(); ++i)
5053 {
5054 IMedium *pIMedium(sfaMedia[i]);
5055 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5056 if (pMedium.isNull())
5057 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5058 SafeArray<BSTR> ids;
5059 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5060 if (FAILED(rc)) return rc;
5061 /* At this point the medium should not have any back references
5062 * anymore. If it has it is attached to another VM and *must* not
5063 * deleted. */
5064 if (ids.size() < 1)
5065 pTask->llMediums.append(pMedium);
5066 }
5067 if (mData->pMachineConfigFile->fileExists())
5068 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5069
5070 pTask->pProgress.createObject();
5071 pTask->pProgress->init(getVirtualBox(),
5072 static_cast<IMachine*>(this) /* aInitiator */,
5073 Bstr(tr("Deleting files")).raw(),
5074 true /* fCancellable */,
5075 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5076 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5077
5078 int vrc = RTThreadCreate(NULL,
5079 Machine::deleteThread,
5080 (void*)pTask,
5081 0,
5082 RTTHREADTYPE_MAIN_WORKER,
5083 0,
5084 "MachineDelete");
5085
5086 pTask->pProgress.queryInterfaceTo(aProgress);
5087
5088 if (RT_FAILURE(vrc))
5089 {
5090 delete pTask;
5091 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5092 }
5093
5094 LogFlowFuncLeave();
5095
5096 return S_OK;
5097}
5098
5099/**
5100 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5101 * calls Machine::deleteTaskWorker() on the actual machine object.
5102 * @param Thread
5103 * @param pvUser
5104 * @return
5105 */
5106/*static*/
5107DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5108{
5109 LogFlowFuncEnter();
5110
5111 DeleteTask *pTask = (DeleteTask*)pvUser;
5112 Assert(pTask);
5113 Assert(pTask->pMachine);
5114 Assert(pTask->pProgress);
5115
5116 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5117 pTask->pProgress->notifyComplete(rc);
5118
5119 delete pTask;
5120
5121 LogFlowFuncLeave();
5122
5123 NOREF(Thread);
5124
5125 return VINF_SUCCESS;
5126}
5127
5128/**
5129 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5130 * @param task
5131 * @return
5132 */
5133HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5134{
5135 AutoCaller autoCaller(this);
5136 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5137
5138 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5139
5140 HRESULT rc = S_OK;
5141
5142 try
5143 {
5144 ULONG uLogHistoryCount = 3;
5145 ComPtr<ISystemProperties> systemProperties;
5146 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5147 if (FAILED(rc)) throw rc;
5148
5149 if (!systemProperties.isNull())
5150 {
5151 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5152 if (FAILED(rc)) throw rc;
5153 }
5154
5155 MachineState_T oldState = mData->mMachineState;
5156 setMachineState(MachineState_SettingUp);
5157 alock.release();
5158 for (size_t i = 0; i < task.llMediums.size(); ++i)
5159 {
5160 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5161 {
5162 AutoCaller mac(pMedium);
5163 if (FAILED(mac.rc())) throw mac.rc();
5164 Utf8Str strLocation = pMedium->getLocationFull();
5165 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5166 if (FAILED(rc)) throw rc;
5167 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5168 }
5169 ComPtr<IProgress> pProgress2;
5170 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5171 if (FAILED(rc)) throw rc;
5172 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5173 if (FAILED(rc)) throw rc;
5174 /* Check the result of the asynchrony process. */
5175 LONG iRc;
5176 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5177 if (FAILED(rc)) throw rc;
5178 /* If the thread of the progress object has an error, then
5179 * retrieve the error info from there, or it'll be lost. */
5180 if (FAILED(iRc))
5181 throw setError(ProgressErrorInfo(pProgress2));
5182 }
5183 setMachineState(oldState);
5184 alock.acquire();
5185
5186 // delete the files pushed on the task list by Machine::Delete()
5187 // (this includes saved states of the machine and snapshots and
5188 // medium storage files from the IMedium list passed in, and the
5189 // machine XML file)
5190 StringsList::const_iterator it = task.llFilesToDelete.begin();
5191 while (it != task.llFilesToDelete.end())
5192 {
5193 const Utf8Str &strFile = *it;
5194 LogFunc(("Deleting file %s\n", strFile.c_str()));
5195 int vrc = RTFileDelete(strFile.c_str());
5196 if (RT_FAILURE(vrc))
5197 throw setError(VBOX_E_IPRT_ERROR,
5198 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5199
5200 ++it;
5201 if (it == task.llFilesToDelete.end())
5202 {
5203 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5204 if (FAILED(rc)) throw rc;
5205 break;
5206 }
5207
5208 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5209 if (FAILED(rc)) throw rc;
5210 }
5211
5212 /* delete the settings only when the file actually exists */
5213 if (mData->pMachineConfigFile->fileExists())
5214 {
5215 /* Delete any backup or uncommitted XML files. Ignore failures.
5216 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5217 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5218 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5219 RTFileDelete(otherXml.c_str());
5220 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5221 RTFileDelete(otherXml.c_str());
5222
5223 /* delete the Logs folder, nothing important should be left
5224 * there (we don't check for errors because the user might have
5225 * some private files there that we don't want to delete) */
5226 Utf8Str logFolder;
5227 getLogFolder(logFolder);
5228 Assert(logFolder.length());
5229 if (RTDirExists(logFolder.c_str()))
5230 {
5231 /* Delete all VBox.log[.N] files from the Logs folder
5232 * (this must be in sync with the rotation logic in
5233 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5234 * files that may have been created by the GUI. */
5235 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5236 logFolder.c_str(), RTPATH_DELIMITER);
5237 RTFileDelete(log.c_str());
5238 log = Utf8StrFmt("%s%cVBox.png",
5239 logFolder.c_str(), RTPATH_DELIMITER);
5240 RTFileDelete(log.c_str());
5241 for (int i = uLogHistoryCount; i > 0; i--)
5242 {
5243 log = Utf8StrFmt("%s%cVBox.log.%d",
5244 logFolder.c_str(), RTPATH_DELIMITER, i);
5245 RTFileDelete(log.c_str());
5246 log = Utf8StrFmt("%s%cVBox.png.%d",
5247 logFolder.c_str(), RTPATH_DELIMITER, i);
5248 RTFileDelete(log.c_str());
5249 }
5250
5251 RTDirRemove(logFolder.c_str());
5252 }
5253
5254 /* delete the Snapshots folder, nothing important should be left
5255 * there (we don't check for errors because the user might have
5256 * some private files there that we don't want to delete) */
5257 Utf8Str strFullSnapshotFolder;
5258 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5259 Assert(!strFullSnapshotFolder.isEmpty());
5260 if (RTDirExists(strFullSnapshotFolder.c_str()))
5261 RTDirRemove(strFullSnapshotFolder.c_str());
5262
5263 // delete the directory that contains the settings file, but only
5264 // if it matches the VM name
5265 Utf8Str settingsDir;
5266 if (isInOwnDir(&settingsDir))
5267 RTDirRemove(settingsDir.c_str());
5268 }
5269
5270 alock.release();
5271
5272 mParent->saveModifiedRegistries();
5273 }
5274 catch (HRESULT aRC) { rc = aRC; }
5275
5276 return rc;
5277}
5278
5279STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5280{
5281 CheckComArgOutPointerValid(aSnapshot);
5282
5283 AutoCaller autoCaller(this);
5284 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5285
5286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5287
5288 ComObjPtr<Snapshot> pSnapshot;
5289 HRESULT rc;
5290
5291 if (!aNameOrId || !*aNameOrId)
5292 // null case (caller wants root snapshot): findSnapshotById() handles this
5293 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5294 else
5295 {
5296 Guid uuid(aNameOrId);
5297 if (uuid.isValid())
5298 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5299 else
5300 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5301 }
5302 pSnapshot.queryInterfaceTo(aSnapshot);
5303
5304 return rc;
5305}
5306
5307STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5308{
5309 CheckComArgStrNotEmptyOrNull(aName);
5310 CheckComArgStrNotEmptyOrNull(aHostPath);
5311
5312 AutoCaller autoCaller(this);
5313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5314
5315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5316
5317 HRESULT rc = checkStateDependency(MutableStateDep);
5318 if (FAILED(rc)) return rc;
5319
5320 Utf8Str strName(aName);
5321
5322 ComObjPtr<SharedFolder> sharedFolder;
5323 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5324 if (SUCCEEDED(rc))
5325 return setError(VBOX_E_OBJECT_IN_USE,
5326 tr("Shared folder named '%s' already exists"),
5327 strName.c_str());
5328
5329 sharedFolder.createObject();
5330 rc = sharedFolder->init(getMachine(),
5331 strName,
5332 aHostPath,
5333 !!aWritable,
5334 !!aAutoMount,
5335 true /* fFailOnError */);
5336 if (FAILED(rc)) return rc;
5337
5338 setModified(IsModified_SharedFolders);
5339 mHWData.backup();
5340 mHWData->mSharedFolders.push_back(sharedFolder);
5341
5342 /* inform the direct session if any */
5343 alock.release();
5344 onSharedFolderChange();
5345
5346 return S_OK;
5347}
5348
5349STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5350{
5351 CheckComArgStrNotEmptyOrNull(aName);
5352
5353 AutoCaller autoCaller(this);
5354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5355
5356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5357
5358 HRESULT rc = checkStateDependency(MutableStateDep);
5359 if (FAILED(rc)) return rc;
5360
5361 ComObjPtr<SharedFolder> sharedFolder;
5362 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5363 if (FAILED(rc)) return rc;
5364
5365 setModified(IsModified_SharedFolders);
5366 mHWData.backup();
5367 mHWData->mSharedFolders.remove(sharedFolder);
5368
5369 /* inform the direct session if any */
5370 alock.release();
5371 onSharedFolderChange();
5372
5373 return S_OK;
5374}
5375
5376STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5377{
5378 CheckComArgOutPointerValid(aCanShow);
5379
5380 /* start with No */
5381 *aCanShow = FALSE;
5382
5383 AutoCaller autoCaller(this);
5384 AssertComRCReturnRC(autoCaller.rc());
5385
5386 ComPtr<IInternalSessionControl> directControl;
5387 {
5388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5389
5390 if (mData->mSession.mState != SessionState_Locked)
5391 return setError(VBOX_E_INVALID_VM_STATE,
5392 tr("Machine is not locked for session (session state: %s)"),
5393 Global::stringifySessionState(mData->mSession.mState));
5394
5395 directControl = mData->mSession.mDirectControl;
5396 }
5397
5398 /* ignore calls made after #OnSessionEnd() is called */
5399 if (!directControl)
5400 return S_OK;
5401
5402 LONG64 dummy;
5403 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5404}
5405
5406STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5407{
5408 CheckComArgOutPointerValid(aWinId);
5409
5410 AutoCaller autoCaller(this);
5411 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5412
5413 ComPtr<IInternalSessionControl> directControl;
5414 {
5415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5416
5417 if (mData->mSession.mState != SessionState_Locked)
5418 return setError(E_FAIL,
5419 tr("Machine is not locked for session (session state: %s)"),
5420 Global::stringifySessionState(mData->mSession.mState));
5421
5422 directControl = mData->mSession.mDirectControl;
5423 }
5424
5425 /* ignore calls made after #OnSessionEnd() is called */
5426 if (!directControl)
5427 return S_OK;
5428
5429 BOOL dummy;
5430 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5431}
5432
5433#ifdef VBOX_WITH_GUEST_PROPS
5434/**
5435 * Look up a guest property in VBoxSVC's internal structures.
5436 */
5437HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5438 BSTR *aValue,
5439 LONG64 *aTimestamp,
5440 BSTR *aFlags) const
5441{
5442 using namespace guestProp;
5443
5444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5445 Utf8Str strName(aName);
5446 HWData::GuestPropertyMap::const_iterator it =
5447 mHWData->mGuestProperties.find(strName);
5448
5449 if (it != mHWData->mGuestProperties.end())
5450 {
5451 char szFlags[MAX_FLAGS_LEN + 1];
5452 it->second.strValue.cloneTo(aValue);
5453 *aTimestamp = it->second.mTimestamp;
5454 writeFlags(it->second.mFlags, szFlags);
5455 Bstr(szFlags).cloneTo(aFlags);
5456 }
5457
5458 return S_OK;
5459}
5460
5461/**
5462 * Query the VM that a guest property belongs to for the property.
5463 * @returns E_ACCESSDENIED if the VM process is not available or not
5464 * currently handling queries and the lookup should then be done in
5465 * VBoxSVC.
5466 */
5467HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5468 BSTR *aValue,
5469 LONG64 *aTimestamp,
5470 BSTR *aFlags) const
5471{
5472 HRESULT rc;
5473 ComPtr<IInternalSessionControl> directControl;
5474 directControl = mData->mSession.mDirectControl;
5475
5476 /* fail if we were called after #OnSessionEnd() is called. This is a
5477 * silly race condition. */
5478
5479 if (!directControl)
5480 rc = E_ACCESSDENIED;
5481 else
5482 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5483 false /* isSetter */,
5484 aValue, aTimestamp, aFlags);
5485 return rc;
5486}
5487#endif // VBOX_WITH_GUEST_PROPS
5488
5489STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5490 BSTR *aValue,
5491 LONG64 *aTimestamp,
5492 BSTR *aFlags)
5493{
5494#ifndef VBOX_WITH_GUEST_PROPS
5495 ReturnComNotImplemented();
5496#else // VBOX_WITH_GUEST_PROPS
5497 CheckComArgStrNotEmptyOrNull(aName);
5498 CheckComArgOutPointerValid(aValue);
5499 CheckComArgOutPointerValid(aTimestamp);
5500 CheckComArgOutPointerValid(aFlags);
5501
5502 AutoCaller autoCaller(this);
5503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5504
5505 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5506 if (rc == E_ACCESSDENIED)
5507 /* The VM is not running or the service is not (yet) accessible */
5508 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5509 return rc;
5510#endif // VBOX_WITH_GUEST_PROPS
5511}
5512
5513STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5514{
5515 LONG64 dummyTimestamp;
5516 Bstr dummyFlags;
5517 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5518}
5519
5520STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5521{
5522 Bstr dummyValue;
5523 Bstr dummyFlags;
5524 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5525}
5526
5527#ifdef VBOX_WITH_GUEST_PROPS
5528/**
5529 * Set a guest property in VBoxSVC's internal structures.
5530 */
5531HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5532 IN_BSTR aFlags)
5533{
5534 using namespace guestProp;
5535
5536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5537 HRESULT rc = S_OK;
5538 HWData::GuestProperty property;
5539 property.mFlags = NILFLAG;
5540
5541 rc = checkStateDependency(MutableStateDep);
5542 if (FAILED(rc)) return rc;
5543
5544 try
5545 {
5546 Utf8Str utf8Name(aName);
5547 Utf8Str utf8Flags(aFlags);
5548 uint32_t fFlags = NILFLAG;
5549 if ( (aFlags != NULL)
5550 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5551 )
5552 return setError(E_INVALIDARG,
5553 tr("Invalid guest property flag values: '%ls'"),
5554 aFlags);
5555
5556 HWData::GuestPropertyMap::iterator it =
5557 mHWData->mGuestProperties.find(utf8Name);
5558
5559 if (it == mHWData->mGuestProperties.end())
5560 {
5561 setModified(IsModified_MachineData);
5562 mHWData.backupEx();
5563
5564 RTTIMESPEC time;
5565 HWData::GuestProperty prop;
5566 prop.strValue = aValue;
5567 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5568 prop.mFlags = fFlags;
5569
5570 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5571 }
5572 else
5573 {
5574 if (it->second.mFlags & (RDONLYHOST))
5575 {
5576 rc = setError(E_ACCESSDENIED,
5577 tr("The property '%ls' cannot be changed by the host"),
5578 aName);
5579 }
5580 else if (aValue)
5581 {
5582 setModified(IsModified_MachineData);
5583 mHWData.backupEx();
5584
5585 /* The backupEx() operation invalidates our iterator,
5586 * so get a new one. */
5587 it = mHWData->mGuestProperties.find(utf8Name);
5588 Assert(it != mHWData->mGuestProperties.end());
5589
5590 RTTIMESPEC time;
5591 it->second.strValue = aValue;
5592 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5593 if (aFlags != NULL)
5594 it->second.mFlags = fFlags;
5595 }
5596 else
5597 {
5598 setModified(IsModified_MachineData);
5599 mHWData.backupEx();
5600
5601 mHWData->mGuestProperties.erase(it);
5602 }
5603 }
5604
5605 if ( SUCCEEDED(rc)
5606 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5607 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5608 RTSTR_MAX,
5609 utf8Name.c_str(),
5610 RTSTR_MAX,
5611 NULL)
5612 )
5613 )
5614 {
5615 alock.release();
5616
5617 mParent->onGuestPropertyChange(mData->mUuid, aName,
5618 aValue ? aValue : Bstr("").raw(),
5619 aFlags ? aFlags : Bstr("").raw());
5620 }
5621 }
5622 catch (std::bad_alloc &)
5623 {
5624 rc = E_OUTOFMEMORY;
5625 }
5626
5627 return rc;
5628}
5629
5630/**
5631 * Set a property on the VM that that property belongs to.
5632 * @returns E_ACCESSDENIED if the VM process is not available or not
5633 * currently handling queries and the setting should then be done in
5634 * VBoxSVC.
5635 */
5636HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5637 IN_BSTR aFlags)
5638{
5639 HRESULT rc;
5640
5641 try
5642 {
5643 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5644
5645 BSTR dummy = NULL; /* will not be changed (setter) */
5646 LONG64 dummy64;
5647 if (!directControl)
5648 rc = E_ACCESSDENIED;
5649 else
5650 /** @todo Fix when adding DeleteGuestProperty(),
5651 see defect. */
5652 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5653 true /* isSetter */,
5654 &dummy, &dummy64, &dummy);
5655 }
5656 catch (std::bad_alloc &)
5657 {
5658 rc = E_OUTOFMEMORY;
5659 }
5660
5661 return rc;
5662}
5663#endif // VBOX_WITH_GUEST_PROPS
5664
5665STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5666 IN_BSTR aFlags)
5667{
5668#ifndef VBOX_WITH_GUEST_PROPS
5669 ReturnComNotImplemented();
5670#else // VBOX_WITH_GUEST_PROPS
5671 CheckComArgStrNotEmptyOrNull(aName);
5672 CheckComArgMaybeNull(aFlags);
5673 CheckComArgMaybeNull(aValue);
5674
5675 AutoCaller autoCaller(this);
5676 if (FAILED(autoCaller.rc()))
5677 return autoCaller.rc();
5678
5679 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5680 if (rc == E_ACCESSDENIED)
5681 /* The VM is not running or the service is not (yet) accessible */
5682 rc = setGuestPropertyToService(aName, aValue, aFlags);
5683 return rc;
5684#endif // VBOX_WITH_GUEST_PROPS
5685}
5686
5687STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5688{
5689 return SetGuestProperty(aName, aValue, NULL);
5690}
5691
5692STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5693{
5694 return SetGuestProperty(aName, NULL, NULL);
5695}
5696
5697#ifdef VBOX_WITH_GUEST_PROPS
5698/**
5699 * Enumerate the guest properties in VBoxSVC's internal structures.
5700 */
5701HRESULT Machine::enumerateGuestPropertiesInService
5702 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5703 ComSafeArrayOut(BSTR, aValues),
5704 ComSafeArrayOut(LONG64, aTimestamps),
5705 ComSafeArrayOut(BSTR, aFlags))
5706{
5707 using namespace guestProp;
5708
5709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5710 Utf8Str strPatterns(aPatterns);
5711
5712 HWData::GuestPropertyMap propMap;
5713
5714 /*
5715 * Look for matching patterns and build up a list.
5716 */
5717 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5718 while (it != mHWData->mGuestProperties.end())
5719 {
5720 if ( strPatterns.isEmpty()
5721 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5722 RTSTR_MAX,
5723 it->first.c_str(),
5724 RTSTR_MAX,
5725 NULL)
5726 )
5727 {
5728 propMap.insert(*it);
5729 }
5730
5731 it++;
5732 }
5733
5734 alock.release();
5735
5736 /*
5737 * And build up the arrays for returning the property information.
5738 */
5739 size_t cEntries = propMap.size();
5740 SafeArray<BSTR> names(cEntries);
5741 SafeArray<BSTR> values(cEntries);
5742 SafeArray<LONG64> timestamps(cEntries);
5743 SafeArray<BSTR> flags(cEntries);
5744 size_t iProp = 0;
5745
5746 it = propMap.begin();
5747 while (it != propMap.end())
5748 {
5749 char szFlags[MAX_FLAGS_LEN + 1];
5750 it->first.cloneTo(&names[iProp]);
5751 it->second.strValue.cloneTo(&values[iProp]);
5752 timestamps[iProp] = it->second.mTimestamp;
5753 writeFlags(it->second.mFlags, szFlags);
5754 Bstr(szFlags).cloneTo(&flags[iProp++]);
5755 it++;
5756 }
5757 names.detachTo(ComSafeArrayOutArg(aNames));
5758 values.detachTo(ComSafeArrayOutArg(aValues));
5759 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5760 flags.detachTo(ComSafeArrayOutArg(aFlags));
5761 return S_OK;
5762}
5763
5764/**
5765 * Enumerate the properties managed by a VM.
5766 * @returns E_ACCESSDENIED if the VM process is not available or not
5767 * currently handling queries and the setting should then be done in
5768 * VBoxSVC.
5769 */
5770HRESULT Machine::enumerateGuestPropertiesOnVM
5771 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5772 ComSafeArrayOut(BSTR, aValues),
5773 ComSafeArrayOut(LONG64, aTimestamps),
5774 ComSafeArrayOut(BSTR, aFlags))
5775{
5776 HRESULT rc;
5777 ComPtr<IInternalSessionControl> directControl;
5778 directControl = mData->mSession.mDirectControl;
5779
5780 if (!directControl)
5781 rc = E_ACCESSDENIED;
5782 else
5783 rc = directControl->EnumerateGuestProperties
5784 (aPatterns, ComSafeArrayOutArg(aNames),
5785 ComSafeArrayOutArg(aValues),
5786 ComSafeArrayOutArg(aTimestamps),
5787 ComSafeArrayOutArg(aFlags));
5788 return rc;
5789}
5790#endif // VBOX_WITH_GUEST_PROPS
5791
5792STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5793 ComSafeArrayOut(BSTR, aNames),
5794 ComSafeArrayOut(BSTR, aValues),
5795 ComSafeArrayOut(LONG64, aTimestamps),
5796 ComSafeArrayOut(BSTR, aFlags))
5797{
5798#ifndef VBOX_WITH_GUEST_PROPS
5799 ReturnComNotImplemented();
5800#else // VBOX_WITH_GUEST_PROPS
5801 CheckComArgMaybeNull(aPatterns);
5802 CheckComArgOutSafeArrayPointerValid(aNames);
5803 CheckComArgOutSafeArrayPointerValid(aValues);
5804 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5805 CheckComArgOutSafeArrayPointerValid(aFlags);
5806
5807 AutoCaller autoCaller(this);
5808 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5809
5810 HRESULT rc = enumerateGuestPropertiesOnVM
5811 (aPatterns, ComSafeArrayOutArg(aNames),
5812 ComSafeArrayOutArg(aValues),
5813 ComSafeArrayOutArg(aTimestamps),
5814 ComSafeArrayOutArg(aFlags));
5815 if (rc == E_ACCESSDENIED)
5816 /* The VM is not running or the service is not (yet) accessible */
5817 rc = enumerateGuestPropertiesInService
5818 (aPatterns, ComSafeArrayOutArg(aNames),
5819 ComSafeArrayOutArg(aValues),
5820 ComSafeArrayOutArg(aTimestamps),
5821 ComSafeArrayOutArg(aFlags));
5822 return rc;
5823#endif // VBOX_WITH_GUEST_PROPS
5824}
5825
5826STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5827 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5828{
5829 MediaData::AttachmentList atts;
5830
5831 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5832 if (FAILED(rc)) return rc;
5833
5834 SafeIfaceArray<IMediumAttachment> attachments(atts);
5835 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5836
5837 return S_OK;
5838}
5839
5840STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5841 LONG aControllerPort,
5842 LONG aDevice,
5843 IMediumAttachment **aAttachment)
5844{
5845 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5846 aControllerName, aControllerPort, aDevice));
5847
5848 CheckComArgStrNotEmptyOrNull(aControllerName);
5849 CheckComArgOutPointerValid(aAttachment);
5850
5851 AutoCaller autoCaller(this);
5852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5853
5854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5855
5856 *aAttachment = NULL;
5857
5858 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5859 aControllerName,
5860 aControllerPort,
5861 aDevice);
5862 if (pAttach.isNull())
5863 return setError(VBOX_E_OBJECT_NOT_FOUND,
5864 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5865 aDevice, aControllerPort, aControllerName);
5866
5867 pAttach.queryInterfaceTo(aAttachment);
5868
5869 return S_OK;
5870}
5871
5872STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5873 StorageBus_T aConnectionType,
5874 IStorageController **controller)
5875{
5876 CheckComArgStrNotEmptyOrNull(aName);
5877
5878 if ( (aConnectionType <= StorageBus_Null)
5879 || (aConnectionType > StorageBus_SAS))
5880 return setError(E_INVALIDARG,
5881 tr("Invalid connection type: %d"),
5882 aConnectionType);
5883
5884 AutoCaller autoCaller(this);
5885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5886
5887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5888
5889 HRESULT rc = checkStateDependency(MutableStateDep);
5890 if (FAILED(rc)) return rc;
5891
5892 /* try to find one with the name first. */
5893 ComObjPtr<StorageController> ctrl;
5894
5895 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5896 if (SUCCEEDED(rc))
5897 return setError(VBOX_E_OBJECT_IN_USE,
5898 tr("Storage controller named '%ls' already exists"),
5899 aName);
5900
5901 ctrl.createObject();
5902
5903 /* get a new instance number for the storage controller */
5904 ULONG ulInstance = 0;
5905 bool fBootable = true;
5906 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5907 it != mStorageControllers->end();
5908 ++it)
5909 {
5910 if ((*it)->getStorageBus() == aConnectionType)
5911 {
5912 ULONG ulCurInst = (*it)->getInstance();
5913
5914 if (ulCurInst >= ulInstance)
5915 ulInstance = ulCurInst + 1;
5916
5917 /* Only one controller of each type can be marked as bootable. */
5918 if ((*it)->getBootable())
5919 fBootable = false;
5920 }
5921 }
5922
5923 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5924 if (FAILED(rc)) return rc;
5925
5926 setModified(IsModified_Storage);
5927 mStorageControllers.backup();
5928 mStorageControllers->push_back(ctrl);
5929
5930 ctrl.queryInterfaceTo(controller);
5931
5932 /* inform the direct session if any */
5933 alock.release();
5934 onStorageControllerChange();
5935
5936 return S_OK;
5937}
5938
5939STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5940 IStorageController **aStorageController)
5941{
5942 CheckComArgStrNotEmptyOrNull(aName);
5943
5944 AutoCaller autoCaller(this);
5945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5946
5947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5948
5949 ComObjPtr<StorageController> ctrl;
5950
5951 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5952 if (SUCCEEDED(rc))
5953 ctrl.queryInterfaceTo(aStorageController);
5954
5955 return rc;
5956}
5957
5958STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5959 IStorageController **aStorageController)
5960{
5961 AutoCaller autoCaller(this);
5962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5963
5964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5965
5966 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5967 it != mStorageControllers->end();
5968 ++it)
5969 {
5970 if ((*it)->getInstance() == aInstance)
5971 {
5972 (*it).queryInterfaceTo(aStorageController);
5973 return S_OK;
5974 }
5975 }
5976
5977 return setError(VBOX_E_OBJECT_NOT_FOUND,
5978 tr("Could not find a storage controller with instance number '%lu'"),
5979 aInstance);
5980}
5981
5982STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5983{
5984 AutoCaller autoCaller(this);
5985 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5986
5987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5988
5989 HRESULT rc = checkStateDependency(MutableStateDep);
5990 if (FAILED(rc)) return rc;
5991
5992 ComObjPtr<StorageController> ctrl;
5993
5994 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5995 if (SUCCEEDED(rc))
5996 {
5997 /* Ensure that only one controller of each type is marked as bootable. */
5998 if (fBootable == TRUE)
5999 {
6000 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6001 it != mStorageControllers->end();
6002 ++it)
6003 {
6004 ComObjPtr<StorageController> aCtrl = (*it);
6005
6006 if ( (aCtrl->getName() != Utf8Str(aName))
6007 && aCtrl->getBootable() == TRUE
6008 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6009 && aCtrl->getControllerType() == ctrl->getControllerType())
6010 {
6011 aCtrl->setBootable(FALSE);
6012 break;
6013 }
6014 }
6015 }
6016
6017 if (SUCCEEDED(rc))
6018 {
6019 ctrl->setBootable(fBootable);
6020 setModified(IsModified_Storage);
6021 }
6022 }
6023
6024 if (SUCCEEDED(rc))
6025 {
6026 /* inform the direct session if any */
6027 alock.release();
6028 onStorageControllerChange();
6029 }
6030
6031 return rc;
6032}
6033
6034STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6035{
6036 CheckComArgStrNotEmptyOrNull(aName);
6037
6038 AutoCaller autoCaller(this);
6039 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6040
6041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6042
6043 HRESULT rc = checkStateDependency(MutableStateDep);
6044 if (FAILED(rc)) return rc;
6045
6046 ComObjPtr<StorageController> ctrl;
6047 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6048 if (FAILED(rc)) return rc;
6049
6050 {
6051 /* find all attached devices to the appropriate storage controller and detach them all */
6052 // make a temporary list because detachDevice invalidates iterators into
6053 // mMediaData->mAttachments
6054 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6055
6056 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6057 it != llAttachments2.end();
6058 ++it)
6059 {
6060 MediumAttachment *pAttachTemp = *it;
6061
6062 AutoCaller localAutoCaller(pAttachTemp);
6063 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6064
6065 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6066
6067 if (pAttachTemp->getControllerName() == aName)
6068 {
6069 rc = detachDevice(pAttachTemp, alock, NULL);
6070 if (FAILED(rc)) return rc;
6071 }
6072 }
6073 }
6074
6075 /* We can remove it now. */
6076 setModified(IsModified_Storage);
6077 mStorageControllers.backup();
6078
6079 ctrl->unshare();
6080
6081 mStorageControllers->remove(ctrl);
6082
6083 /* inform the direct session if any */
6084 alock.release();
6085 onStorageControllerChange();
6086
6087 return S_OK;
6088}
6089
6090STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6091 ULONG *puOriginX,
6092 ULONG *puOriginY,
6093 ULONG *puWidth,
6094 ULONG *puHeight,
6095 BOOL *pfEnabled)
6096{
6097 LogFlowThisFunc(("\n"));
6098
6099 CheckComArgNotNull(puOriginX);
6100 CheckComArgNotNull(puOriginY);
6101 CheckComArgNotNull(puWidth);
6102 CheckComArgNotNull(puHeight);
6103 CheckComArgNotNull(pfEnabled);
6104
6105 uint32_t u32OriginX= 0;
6106 uint32_t u32OriginY= 0;
6107 uint32_t u32Width = 0;
6108 uint32_t u32Height = 0;
6109 uint16_t u16Flags = 0;
6110
6111 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6112 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6113 if (RT_FAILURE(vrc))
6114 {
6115#ifdef RT_OS_WINDOWS
6116 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6117 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6118 * So just assign fEnable to TRUE again.
6119 * The right fix would be to change GUI API wrappers to make sure that parameters
6120 * are changed only if API succeeds.
6121 */
6122 *pfEnabled = TRUE;
6123#endif
6124 return setError(VBOX_E_IPRT_ERROR,
6125 tr("Saved guest size is not available (%Rrc)"),
6126 vrc);
6127 }
6128
6129 *puOriginX = u32OriginX;
6130 *puOriginY = u32OriginY;
6131 *puWidth = u32Width;
6132 *puHeight = u32Height;
6133 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6134
6135 return S_OK;
6136}
6137
6138STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6139{
6140 LogFlowThisFunc(("\n"));
6141
6142 CheckComArgNotNull(aSize);
6143 CheckComArgNotNull(aWidth);
6144 CheckComArgNotNull(aHeight);
6145
6146 if (aScreenId != 0)
6147 return E_NOTIMPL;
6148
6149 AutoCaller autoCaller(this);
6150 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6151
6152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6153
6154 uint8_t *pu8Data = NULL;
6155 uint32_t cbData = 0;
6156 uint32_t u32Width = 0;
6157 uint32_t u32Height = 0;
6158
6159 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6160
6161 if (RT_FAILURE(vrc))
6162 return setError(VBOX_E_IPRT_ERROR,
6163 tr("Saved screenshot data is not available (%Rrc)"),
6164 vrc);
6165
6166 *aSize = cbData;
6167 *aWidth = u32Width;
6168 *aHeight = u32Height;
6169
6170 freeSavedDisplayScreenshot(pu8Data);
6171
6172 return S_OK;
6173}
6174
6175STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6176{
6177 LogFlowThisFunc(("\n"));
6178
6179 CheckComArgNotNull(aWidth);
6180 CheckComArgNotNull(aHeight);
6181 CheckComArgOutSafeArrayPointerValid(aData);
6182
6183 if (aScreenId != 0)
6184 return E_NOTIMPL;
6185
6186 AutoCaller autoCaller(this);
6187 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6188
6189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6190
6191 uint8_t *pu8Data = NULL;
6192 uint32_t cbData = 0;
6193 uint32_t u32Width = 0;
6194 uint32_t u32Height = 0;
6195
6196 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6197
6198 if (RT_FAILURE(vrc))
6199 return setError(VBOX_E_IPRT_ERROR,
6200 tr("Saved screenshot data is not available (%Rrc)"),
6201 vrc);
6202
6203 *aWidth = u32Width;
6204 *aHeight = u32Height;
6205
6206 com::SafeArray<BYTE> bitmap(cbData);
6207 /* Convert pixels to format expected by the API caller. */
6208 if (aBGR)
6209 {
6210 /* [0] B, [1] G, [2] R, [3] A. */
6211 for (unsigned i = 0; i < cbData; i += 4)
6212 {
6213 bitmap[i] = pu8Data[i];
6214 bitmap[i + 1] = pu8Data[i + 1];
6215 bitmap[i + 2] = pu8Data[i + 2];
6216 bitmap[i + 3] = 0xff;
6217 }
6218 }
6219 else
6220 {
6221 /* [0] R, [1] G, [2] B, [3] A. */
6222 for (unsigned i = 0; i < cbData; i += 4)
6223 {
6224 bitmap[i] = pu8Data[i + 2];
6225 bitmap[i + 1] = pu8Data[i + 1];
6226 bitmap[i + 2] = pu8Data[i];
6227 bitmap[i + 3] = 0xff;
6228 }
6229 }
6230 bitmap.detachTo(ComSafeArrayOutArg(aData));
6231
6232 freeSavedDisplayScreenshot(pu8Data);
6233
6234 return S_OK;
6235}
6236
6237
6238STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6239{
6240 LogFlowThisFunc(("\n"));
6241
6242 CheckComArgNotNull(aWidth);
6243 CheckComArgNotNull(aHeight);
6244 CheckComArgOutSafeArrayPointerValid(aData);
6245
6246 if (aScreenId != 0)
6247 return E_NOTIMPL;
6248
6249 AutoCaller autoCaller(this);
6250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6251
6252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6253
6254 uint8_t *pu8Data = NULL;
6255 uint32_t cbData = 0;
6256 uint32_t u32Width = 0;
6257 uint32_t u32Height = 0;
6258
6259 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6260
6261 if (RT_FAILURE(vrc))
6262 return setError(VBOX_E_IPRT_ERROR,
6263 tr("Saved screenshot data is not available (%Rrc)"),
6264 vrc);
6265
6266 *aWidth = u32Width;
6267 *aHeight = u32Height;
6268
6269 HRESULT rc = S_OK;
6270 uint8_t *pu8PNG = NULL;
6271 uint32_t cbPNG = 0;
6272 uint32_t cxPNG = 0;
6273 uint32_t cyPNG = 0;
6274
6275 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6276
6277 if (RT_SUCCESS(vrc))
6278 {
6279 com::SafeArray<BYTE> screenData(cbPNG);
6280 screenData.initFrom(pu8PNG, cbPNG);
6281 if (pu8PNG)
6282 RTMemFree(pu8PNG);
6283 screenData.detachTo(ComSafeArrayOutArg(aData));
6284 }
6285 else
6286 {
6287 if (pu8PNG)
6288 RTMemFree(pu8PNG);
6289 return setError(VBOX_E_IPRT_ERROR,
6290 tr("Could not convert screenshot to PNG (%Rrc)"),
6291 vrc);
6292 }
6293
6294 freeSavedDisplayScreenshot(pu8Data);
6295
6296 return rc;
6297}
6298
6299STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6300{
6301 LogFlowThisFunc(("\n"));
6302
6303 CheckComArgNotNull(aSize);
6304 CheckComArgNotNull(aWidth);
6305 CheckComArgNotNull(aHeight);
6306
6307 if (aScreenId != 0)
6308 return E_NOTIMPL;
6309
6310 AutoCaller autoCaller(this);
6311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6312
6313 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6314
6315 uint8_t *pu8Data = NULL;
6316 uint32_t cbData = 0;
6317 uint32_t u32Width = 0;
6318 uint32_t u32Height = 0;
6319
6320 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6321
6322 if (RT_FAILURE(vrc))
6323 return setError(VBOX_E_IPRT_ERROR,
6324 tr("Saved screenshot data is not available (%Rrc)"),
6325 vrc);
6326
6327 *aSize = cbData;
6328 *aWidth = u32Width;
6329 *aHeight = u32Height;
6330
6331 freeSavedDisplayScreenshot(pu8Data);
6332
6333 return S_OK;
6334}
6335
6336STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6337{
6338 LogFlowThisFunc(("\n"));
6339
6340 CheckComArgNotNull(aWidth);
6341 CheckComArgNotNull(aHeight);
6342 CheckComArgOutSafeArrayPointerValid(aData);
6343
6344 if (aScreenId != 0)
6345 return E_NOTIMPL;
6346
6347 AutoCaller autoCaller(this);
6348 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6349
6350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 uint8_t *pu8Data = NULL;
6353 uint32_t cbData = 0;
6354 uint32_t u32Width = 0;
6355 uint32_t u32Height = 0;
6356
6357 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6358
6359 if (RT_FAILURE(vrc))
6360 return setError(VBOX_E_IPRT_ERROR,
6361 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6362 vrc);
6363
6364 *aWidth = u32Width;
6365 *aHeight = u32Height;
6366
6367 com::SafeArray<BYTE> png(cbData);
6368 png.initFrom(pu8Data, cbData);
6369 png.detachTo(ComSafeArrayOutArg(aData));
6370
6371 freeSavedDisplayScreenshot(pu8Data);
6372
6373 return S_OK;
6374}
6375
6376STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6377{
6378 HRESULT rc = S_OK;
6379 LogFlowThisFunc(("\n"));
6380
6381 AutoCaller autoCaller(this);
6382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6383
6384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6385
6386 if (!mHWData->mCPUHotPlugEnabled)
6387 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6388
6389 if (aCpu >= mHWData->mCPUCount)
6390 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6391
6392 if (mHWData->mCPUAttached[aCpu])
6393 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6394
6395 alock.release();
6396 rc = onCPUChange(aCpu, false);
6397 alock.acquire();
6398 if (FAILED(rc)) return rc;
6399
6400 setModified(IsModified_MachineData);
6401 mHWData.backup();
6402 mHWData->mCPUAttached[aCpu] = true;
6403
6404 /* Save settings if online */
6405 if (Global::IsOnline(mData->mMachineState))
6406 saveSettings(NULL);
6407
6408 return S_OK;
6409}
6410
6411STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6412{
6413 HRESULT rc = S_OK;
6414 LogFlowThisFunc(("\n"));
6415
6416 AutoCaller autoCaller(this);
6417 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6418
6419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6420
6421 if (!mHWData->mCPUHotPlugEnabled)
6422 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6423
6424 if (aCpu >= SchemaDefs::MaxCPUCount)
6425 return setError(E_INVALIDARG,
6426 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6427 SchemaDefs::MaxCPUCount);
6428
6429 if (!mHWData->mCPUAttached[aCpu])
6430 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6431
6432 /* CPU 0 can't be detached */
6433 if (aCpu == 0)
6434 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6435
6436 alock.release();
6437 rc = onCPUChange(aCpu, true);
6438 alock.acquire();
6439 if (FAILED(rc)) return rc;
6440
6441 setModified(IsModified_MachineData);
6442 mHWData.backup();
6443 mHWData->mCPUAttached[aCpu] = false;
6444
6445 /* Save settings if online */
6446 if (Global::IsOnline(mData->mMachineState))
6447 saveSettings(NULL);
6448
6449 return S_OK;
6450}
6451
6452STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6453{
6454 LogFlowThisFunc(("\n"));
6455
6456 CheckComArgNotNull(aCpuAttached);
6457
6458 *aCpuAttached = false;
6459
6460 AutoCaller autoCaller(this);
6461 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6462
6463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6464
6465 /* If hotplug is enabled the CPU is always enabled. */
6466 if (!mHWData->mCPUHotPlugEnabled)
6467 {
6468 if (aCpu < mHWData->mCPUCount)
6469 *aCpuAttached = true;
6470 }
6471 else
6472 {
6473 if (aCpu < SchemaDefs::MaxCPUCount)
6474 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6475 }
6476
6477 return S_OK;
6478}
6479
6480STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6481{
6482 CheckComArgOutPointerValid(aName);
6483
6484 AutoCaller autoCaller(this);
6485 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6486
6487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6488
6489 Utf8Str log = queryLogFilename(aIdx);
6490 if (!RTFileExists(log.c_str()))
6491 log.setNull();
6492 log.cloneTo(aName);
6493
6494 return S_OK;
6495}
6496
6497STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6498{
6499 LogFlowThisFunc(("\n"));
6500 CheckComArgOutSafeArrayPointerValid(aData);
6501 if (aSize < 0)
6502 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6503
6504 AutoCaller autoCaller(this);
6505 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6506
6507 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6508
6509 HRESULT rc = S_OK;
6510 Utf8Str log = queryLogFilename(aIdx);
6511
6512 /* do not unnecessarily hold the lock while doing something which does
6513 * not need the lock and potentially takes a long time. */
6514 alock.release();
6515
6516 /* Limit the chunk size to 32K for now, as that gives better performance
6517 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6518 * One byte expands to approx. 25 bytes of breathtaking XML. */
6519 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6520 com::SafeArray<BYTE> logData(cbData);
6521
6522 RTFILE LogFile;
6523 int vrc = RTFileOpen(&LogFile, log.c_str(),
6524 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6525 if (RT_SUCCESS(vrc))
6526 {
6527 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6528 if (RT_SUCCESS(vrc))
6529 logData.resize(cbData);
6530 else
6531 rc = setError(VBOX_E_IPRT_ERROR,
6532 tr("Could not read log file '%s' (%Rrc)"),
6533 log.c_str(), vrc);
6534 RTFileClose(LogFile);
6535 }
6536 else
6537 rc = setError(VBOX_E_IPRT_ERROR,
6538 tr("Could not open log file '%s' (%Rrc)"),
6539 log.c_str(), vrc);
6540
6541 if (FAILED(rc))
6542 logData.resize(0);
6543 logData.detachTo(ComSafeArrayOutArg(aData));
6544
6545 return rc;
6546}
6547
6548
6549/**
6550 * Currently this method doesn't attach device to the running VM,
6551 * just makes sure it's plugged on next VM start.
6552 */
6553STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6554{
6555 AutoCaller autoCaller(this);
6556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6557
6558 // lock scope
6559 {
6560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6561
6562 HRESULT rc = checkStateDependency(MutableStateDep);
6563 if (FAILED(rc)) return rc;
6564
6565 ChipsetType_T aChipset = ChipsetType_PIIX3;
6566 COMGETTER(ChipsetType)(&aChipset);
6567
6568 if (aChipset != ChipsetType_ICH9)
6569 {
6570 return setError(E_INVALIDARG,
6571 tr("Host PCI attachment only supported with ICH9 chipset"));
6572 }
6573
6574 // check if device with this host PCI address already attached
6575 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6576 it != mHWData->mPCIDeviceAssignments.end();
6577 ++it)
6578 {
6579 LONG iHostAddress = -1;
6580 ComPtr<PCIDeviceAttachment> pAttach;
6581 pAttach = *it;
6582 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6583 if (iHostAddress == hostAddress)
6584 return setError(E_INVALIDARG,
6585 tr("Device with host PCI address already attached to this VM"));
6586 }
6587
6588 ComObjPtr<PCIDeviceAttachment> pda;
6589 char name[32];
6590
6591 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6592 Bstr bname(name);
6593 pda.createObject();
6594 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6595 setModified(IsModified_MachineData);
6596 mHWData.backup();
6597 mHWData->mPCIDeviceAssignments.push_back(pda);
6598 }
6599
6600 return S_OK;
6601}
6602
6603/**
6604 * Currently this method doesn't detach device from the running VM,
6605 * just makes sure it's not plugged on next VM start.
6606 */
6607STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6608{
6609 AutoCaller autoCaller(this);
6610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6611
6612 ComObjPtr<PCIDeviceAttachment> pAttach;
6613 bool fRemoved = false;
6614 HRESULT rc;
6615
6616 // lock scope
6617 {
6618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6619
6620 rc = checkStateDependency(MutableStateDep);
6621 if (FAILED(rc)) return rc;
6622
6623 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6624 it != mHWData->mPCIDeviceAssignments.end();
6625 ++it)
6626 {
6627 LONG iHostAddress = -1;
6628 pAttach = *it;
6629 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6630 if (iHostAddress != -1 && iHostAddress == hostAddress)
6631 {
6632 setModified(IsModified_MachineData);
6633 mHWData.backup();
6634 mHWData->mPCIDeviceAssignments.remove(pAttach);
6635 fRemoved = true;
6636 break;
6637 }
6638 }
6639 }
6640
6641
6642 /* Fire event outside of the lock */
6643 if (fRemoved)
6644 {
6645 Assert(!pAttach.isNull());
6646 ComPtr<IEventSource> es;
6647 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6648 Assert(SUCCEEDED(rc));
6649 Bstr mid;
6650 rc = this->COMGETTER(Id)(mid.asOutParam());
6651 Assert(SUCCEEDED(rc));
6652 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6653 }
6654
6655 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6656 tr("No host PCI device %08x attached"),
6657 hostAddress
6658 );
6659}
6660
6661STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6662{
6663 CheckComArgOutSafeArrayPointerValid(aAssignments);
6664
6665 AutoCaller autoCaller(this);
6666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6667
6668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6669
6670 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6671 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6672
6673 return S_OK;
6674}
6675
6676STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6677{
6678 CheckComArgOutPointerValid(aBandwidthControl);
6679
6680 AutoCaller autoCaller(this);
6681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6682
6683 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6684
6685 return S_OK;
6686}
6687
6688STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6689{
6690 CheckComArgOutPointerValid(pfEnabled);
6691 AutoCaller autoCaller(this);
6692 HRESULT hrc = autoCaller.rc();
6693 if (SUCCEEDED(hrc))
6694 {
6695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6696 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6697 }
6698 return hrc;
6699}
6700
6701STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6702{
6703 AutoCaller autoCaller(this);
6704 HRESULT hrc = autoCaller.rc();
6705 if (SUCCEEDED(hrc))
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708 hrc = checkStateDependency(MutableStateDep);
6709 if (SUCCEEDED(hrc))
6710 {
6711 hrc = mHWData.backupEx();
6712 if (SUCCEEDED(hrc))
6713 {
6714 setModified(IsModified_MachineData);
6715 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6716 }
6717 }
6718 }
6719 return hrc;
6720}
6721
6722STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6723{
6724 CheckComArgOutPointerValid(pbstrConfig);
6725 AutoCaller autoCaller(this);
6726 HRESULT hrc = autoCaller.rc();
6727 if (SUCCEEDED(hrc))
6728 {
6729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6730 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6731 }
6732 return hrc;
6733}
6734
6735STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6736{
6737 CheckComArgStr(bstrConfig);
6738 AutoCaller autoCaller(this);
6739 HRESULT hrc = autoCaller.rc();
6740 if (SUCCEEDED(hrc))
6741 {
6742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6743 hrc = checkStateDependency(MutableStateDep);
6744 if (SUCCEEDED(hrc))
6745 {
6746 hrc = mHWData.backupEx();
6747 if (SUCCEEDED(hrc))
6748 {
6749 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6750 if (SUCCEEDED(hrc))
6751 setModified(IsModified_MachineData);
6752 }
6753 }
6754 }
6755 return hrc;
6756
6757}
6758
6759STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6760{
6761 CheckComArgOutPointerValid(pfAllow);
6762 AutoCaller autoCaller(this);
6763 HRESULT hrc = autoCaller.rc();
6764 if (SUCCEEDED(hrc))
6765 {
6766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6767 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6768 }
6769 return hrc;
6770}
6771
6772STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6773{
6774 AutoCaller autoCaller(this);
6775 HRESULT hrc = autoCaller.rc();
6776 if (SUCCEEDED(hrc))
6777 {
6778 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6779 hrc = checkStateDependency(MutableStateDep);
6780 if (SUCCEEDED(hrc))
6781 {
6782 hrc = mHWData.backupEx();
6783 if (SUCCEEDED(hrc))
6784 {
6785 setModified(IsModified_MachineData);
6786 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6787 }
6788 }
6789 }
6790 return hrc;
6791}
6792
6793STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6794{
6795 CheckComArgOutPointerValid(pfEnabled);
6796 AutoCaller autoCaller(this);
6797 HRESULT hrc = autoCaller.rc();
6798 if (SUCCEEDED(hrc))
6799 {
6800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6802 }
6803 return hrc;
6804}
6805
6806STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6807{
6808 AutoCaller autoCaller(this);
6809 HRESULT hrc = autoCaller.rc();
6810 if (SUCCEEDED(hrc))
6811 {
6812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6813 hrc = checkStateDependency(MutableStateDep);
6814 if ( SUCCEEDED(hrc)
6815 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6816 {
6817 AutostartDb *autostartDb = mParent->getAutostartDb();
6818 int vrc;
6819
6820 if (fEnabled)
6821 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6822 else
6823 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6824
6825 if (RT_SUCCESS(vrc))
6826 {
6827 hrc = mHWData.backupEx();
6828 if (SUCCEEDED(hrc))
6829 {
6830 setModified(IsModified_MachineData);
6831 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6832 }
6833 }
6834 else if (vrc == VERR_NOT_SUPPORTED)
6835 hrc = setError(VBOX_E_NOT_SUPPORTED,
6836 tr("The VM autostart feature is not supported on this platform"));
6837 else if (vrc == VERR_PATH_NOT_FOUND)
6838 hrc = setError(E_FAIL,
6839 tr("The path to the autostart database is not set"));
6840 else
6841 hrc = setError(E_UNEXPECTED,
6842 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6843 fEnabled ? "Adding" : "Removing",
6844 mUserData->s.strName.c_str(), vrc);
6845 }
6846 }
6847 return hrc;
6848}
6849
6850STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6851{
6852 CheckComArgOutPointerValid(puDelay);
6853 AutoCaller autoCaller(this);
6854 HRESULT hrc = autoCaller.rc();
6855 if (SUCCEEDED(hrc))
6856 {
6857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6858 *puDelay = mHWData->mAutostart.uAutostartDelay;
6859 }
6860 return hrc;
6861}
6862
6863STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6864{
6865 AutoCaller autoCaller(this);
6866 HRESULT hrc = autoCaller.rc();
6867 if (SUCCEEDED(hrc))
6868 {
6869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6870 hrc = checkStateDependency(MutableStateDep);
6871 if (SUCCEEDED(hrc))
6872 {
6873 hrc = mHWData.backupEx();
6874 if (SUCCEEDED(hrc))
6875 {
6876 setModified(IsModified_MachineData);
6877 mHWData->mAutostart.uAutostartDelay = uDelay;
6878 }
6879 }
6880 }
6881 return hrc;
6882}
6883
6884STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6885{
6886 CheckComArgOutPointerValid(penmAutostopType);
6887 AutoCaller autoCaller(this);
6888 HRESULT hrc = autoCaller.rc();
6889 if (SUCCEEDED(hrc))
6890 {
6891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6892 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6893 }
6894 return hrc;
6895}
6896
6897STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6898{
6899 AutoCaller autoCaller(this);
6900 HRESULT hrc = autoCaller.rc();
6901 if (SUCCEEDED(hrc))
6902 {
6903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6904 hrc = checkStateDependency(MutableStateDep);
6905 if ( SUCCEEDED(hrc)
6906 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6907 {
6908 AutostartDb *autostartDb = mParent->getAutostartDb();
6909 int vrc;
6910
6911 if (enmAutostopType != AutostopType_Disabled)
6912 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6913 else
6914 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6915
6916 if (RT_SUCCESS(vrc))
6917 {
6918 hrc = mHWData.backupEx();
6919 if (SUCCEEDED(hrc))
6920 {
6921 setModified(IsModified_MachineData);
6922 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6923 }
6924 }
6925 else if (vrc == VERR_NOT_SUPPORTED)
6926 hrc = setError(VBOX_E_NOT_SUPPORTED,
6927 tr("The VM autostop feature is not supported on this platform"));
6928 else if (vrc == VERR_PATH_NOT_FOUND)
6929 hrc = setError(E_FAIL,
6930 tr("The path to the autostart database is not set"));
6931 else
6932 hrc = setError(E_UNEXPECTED,
6933 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6934 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6935 mUserData->s.strName.c_str(), vrc);
6936 }
6937 }
6938 return hrc;
6939}
6940
6941
6942STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6943{
6944 LogFlowFuncEnter();
6945
6946 CheckComArgNotNull(pTarget);
6947 CheckComArgOutPointerValid(pProgress);
6948
6949 /* Convert the options. */
6950 RTCList<CloneOptions_T> optList;
6951 if (options != NULL)
6952 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6953
6954 if (optList.contains(CloneOptions_Link))
6955 {
6956 if (!isSnapshotMachine())
6957 return setError(E_INVALIDARG,
6958 tr("Linked clone can only be created from a snapshot"));
6959 if (mode != CloneMode_MachineState)
6960 return setError(E_INVALIDARG,
6961 tr("Linked clone can only be created for a single machine state"));
6962 }
6963 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6964
6965 AutoCaller autoCaller(this);
6966 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6967
6968
6969 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6970
6971 HRESULT rc = pWorker->start(pProgress);
6972
6973 LogFlowFuncLeave();
6974
6975 return rc;
6976}
6977
6978// public methods for internal purposes
6979/////////////////////////////////////////////////////////////////////////////
6980
6981/**
6982 * Adds the given IsModified_* flag to the dirty flags of the machine.
6983 * This must be called either during loadSettings or under the machine write lock.
6984 * @param fl
6985 */
6986void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6987{
6988 mData->flModifications |= fl;
6989 if (fAllowStateModification && isStateModificationAllowed())
6990 mData->mCurrentStateModified = true;
6991}
6992
6993/**
6994 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6995 * care of the write locking.
6996 *
6997 * @param fModifications The flag to add.
6998 */
6999void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7000{
7001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7002 setModified(fModification, fAllowStateModification);
7003}
7004
7005/**
7006 * Saves the registry entry of this machine to the given configuration node.
7007 *
7008 * @param aEntryNode Node to save the registry entry to.
7009 *
7010 * @note locks this object for reading.
7011 */
7012HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7013{
7014 AutoLimitedCaller autoCaller(this);
7015 AssertComRCReturnRC(autoCaller.rc());
7016
7017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7018
7019 data.uuid = mData->mUuid;
7020 data.strSettingsFile = mData->m_strConfigFile;
7021
7022 return S_OK;
7023}
7024
7025/**
7026 * Calculates the absolute path of the given path taking the directory of the
7027 * machine settings file as the current directory.
7028 *
7029 * @param aPath Path to calculate the absolute path for.
7030 * @param aResult Where to put the result (used only on success, can be the
7031 * same Utf8Str instance as passed in @a aPath).
7032 * @return IPRT result.
7033 *
7034 * @note Locks this object for reading.
7035 */
7036int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7037{
7038 AutoCaller autoCaller(this);
7039 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7040
7041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7042
7043 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7044
7045 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7046
7047 strSettingsDir.stripFilename();
7048 char folder[RTPATH_MAX];
7049 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7050 if (RT_SUCCESS(vrc))
7051 aResult = folder;
7052
7053 return vrc;
7054}
7055
7056/**
7057 * Copies strSource to strTarget, making it relative to the machine folder
7058 * if it is a subdirectory thereof, or simply copying it otherwise.
7059 *
7060 * @param strSource Path to evaluate and copy.
7061 * @param strTarget Buffer to receive target path.
7062 *
7063 * @note Locks this object for reading.
7064 */
7065void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7066 Utf8Str &strTarget)
7067{
7068 AutoCaller autoCaller(this);
7069 AssertComRCReturn(autoCaller.rc(), (void)0);
7070
7071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7072
7073 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7074 // use strTarget as a temporary buffer to hold the machine settings dir
7075 strTarget = mData->m_strConfigFileFull;
7076 strTarget.stripFilename();
7077 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7078 {
7079 // is relative: then append what's left
7080 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7081 // for empty paths (only possible for subdirs) use "." to avoid
7082 // triggering default settings for not present config attributes.
7083 if (strTarget.isEmpty())
7084 strTarget = ".";
7085 }
7086 else
7087 // is not relative: then overwrite
7088 strTarget = strSource;
7089}
7090
7091/**
7092 * Returns the full path to the machine's log folder in the
7093 * \a aLogFolder argument.
7094 */
7095void Machine::getLogFolder(Utf8Str &aLogFolder)
7096{
7097 AutoCaller autoCaller(this);
7098 AssertComRCReturnVoid(autoCaller.rc());
7099
7100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 char szTmp[RTPATH_MAX];
7103 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7104 if (RT_SUCCESS(vrc))
7105 {
7106 if (szTmp[0] && !mUserData.isNull())
7107 {
7108 char szTmp2[RTPATH_MAX];
7109 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7110 if (RT_SUCCESS(vrc))
7111 aLogFolder = BstrFmt("%s%c%s",
7112 szTmp2,
7113 RTPATH_DELIMITER,
7114 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7115 }
7116 else
7117 vrc = VERR_PATH_IS_RELATIVE;
7118 }
7119
7120 if (RT_FAILURE(vrc))
7121 {
7122 // fallback if VBOX_USER_LOGHOME is not set or invalid
7123 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7124 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7125 aLogFolder.append(RTPATH_DELIMITER);
7126 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7127 }
7128}
7129
7130/**
7131 * Returns the full path to the machine's log file for an given index.
7132 */
7133Utf8Str Machine::queryLogFilename(ULONG idx)
7134{
7135 Utf8Str logFolder;
7136 getLogFolder(logFolder);
7137 Assert(logFolder.length());
7138 Utf8Str log;
7139 if (idx == 0)
7140 log = Utf8StrFmt("%s%cVBox.log",
7141 logFolder.c_str(), RTPATH_DELIMITER);
7142 else
7143 log = Utf8StrFmt("%s%cVBox.log.%d",
7144 logFolder.c_str(), RTPATH_DELIMITER, idx);
7145 return log;
7146}
7147
7148/**
7149 * Composes a unique saved state filename based on the current system time. The filename is
7150 * granular to the second so this will work so long as no more than one snapshot is taken on
7151 * a machine per second.
7152 *
7153 * Before version 4.1, we used this formula for saved state files:
7154 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7155 * which no longer works because saved state files can now be shared between the saved state of the
7156 * "saved" machine and an online snapshot, and the following would cause problems:
7157 * 1) save machine
7158 * 2) create online snapshot from that machine state --> reusing saved state file
7159 * 3) save machine again --> filename would be reused, breaking the online snapshot
7160 *
7161 * So instead we now use a timestamp.
7162 *
7163 * @param str
7164 */
7165void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7166{
7167 AutoCaller autoCaller(this);
7168 AssertComRCReturnVoid(autoCaller.rc());
7169
7170 {
7171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7172 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7173 }
7174
7175 RTTIMESPEC ts;
7176 RTTimeNow(&ts);
7177 RTTIME time;
7178 RTTimeExplode(&time, &ts);
7179
7180 strStateFilePath += RTPATH_DELIMITER;
7181 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7182 time.i32Year, time.u8Month, time.u8MonthDay,
7183 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7184}
7185
7186/**
7187 * @note Locks this object for writing, calls the client process
7188 * (inside the lock).
7189 */
7190HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7191 const Utf8Str &strType,
7192 const Utf8Str &strEnvironment,
7193 ProgressProxy *aProgress)
7194{
7195 LogFlowThisFuncEnter();
7196
7197 AssertReturn(aControl, E_FAIL);
7198 AssertReturn(aProgress, E_FAIL);
7199
7200 AutoCaller autoCaller(this);
7201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7202
7203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7204
7205 if (!mData->mRegistered)
7206 return setError(E_UNEXPECTED,
7207 tr("The machine '%s' is not registered"),
7208 mUserData->s.strName.c_str());
7209
7210 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7211
7212 if ( mData->mSession.mState == SessionState_Locked
7213 || mData->mSession.mState == SessionState_Spawning
7214 || mData->mSession.mState == SessionState_Unlocking)
7215 return setError(VBOX_E_INVALID_OBJECT_STATE,
7216 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7217 mUserData->s.strName.c_str());
7218
7219 /* may not be busy */
7220 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7221
7222 /* get the path to the executable */
7223 char szPath[RTPATH_MAX];
7224 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7225 size_t sz = strlen(szPath);
7226 szPath[sz++] = RTPATH_DELIMITER;
7227 szPath[sz] = 0;
7228 char *cmd = szPath + sz;
7229 sz = RTPATH_MAX - sz;
7230
7231 int vrc = VINF_SUCCESS;
7232 RTPROCESS pid = NIL_RTPROCESS;
7233
7234 RTENV env = RTENV_DEFAULT;
7235
7236 if (!strEnvironment.isEmpty())
7237 {
7238 char *newEnvStr = NULL;
7239
7240 do
7241 {
7242 /* clone the current environment */
7243 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7244 AssertRCBreakStmt(vrc2, vrc = vrc2);
7245
7246 newEnvStr = RTStrDup(strEnvironment.c_str());
7247 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7248
7249 /* put new variables to the environment
7250 * (ignore empty variable names here since RTEnv API
7251 * intentionally doesn't do that) */
7252 char *var = newEnvStr;
7253 for (char *p = newEnvStr; *p; ++p)
7254 {
7255 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7256 {
7257 *p = '\0';
7258 if (*var)
7259 {
7260 char *val = strchr(var, '=');
7261 if (val)
7262 {
7263 *val++ = '\0';
7264 vrc2 = RTEnvSetEx(env, var, val);
7265 }
7266 else
7267 vrc2 = RTEnvUnsetEx(env, var);
7268 if (RT_FAILURE(vrc2))
7269 break;
7270 }
7271 var = p + 1;
7272 }
7273 }
7274 if (RT_SUCCESS(vrc2) && *var)
7275 vrc2 = RTEnvPutEx(env, var);
7276
7277 AssertRCBreakStmt(vrc2, vrc = vrc2);
7278 }
7279 while (0);
7280
7281 if (newEnvStr != NULL)
7282 RTStrFree(newEnvStr);
7283 }
7284
7285 /* Qt is default */
7286#ifdef VBOX_WITH_QTGUI
7287 if (strType == "gui" || strType == "GUI/Qt")
7288 {
7289# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7290 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7291# else
7292 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7293# endif
7294 Assert(sz >= sizeof(VirtualBox_exe));
7295 strcpy(cmd, VirtualBox_exe);
7296
7297 Utf8Str idStr = mData->mUuid.toString();
7298 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7299 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7300 }
7301#else /* !VBOX_WITH_QTGUI */
7302 if (0)
7303 ;
7304#endif /* VBOX_WITH_QTGUI */
7305
7306 else
7307
7308#ifdef VBOX_WITH_VBOXSDL
7309 if (strType == "sdl" || strType == "GUI/SDL")
7310 {
7311 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7312 Assert(sz >= sizeof(VBoxSDL_exe));
7313 strcpy(cmd, VBoxSDL_exe);
7314
7315 Utf8Str idStr = mData->mUuid.toString();
7316 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7317 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7318 }
7319#else /* !VBOX_WITH_VBOXSDL */
7320 if (0)
7321 ;
7322#endif /* !VBOX_WITH_VBOXSDL */
7323
7324 else
7325
7326#ifdef VBOX_WITH_HEADLESS
7327 if ( strType == "headless"
7328 || strType == "capture"
7329 || strType == "vrdp" /* Deprecated. Same as headless. */
7330 )
7331 {
7332 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7333 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7334 * and a VM works even if the server has not been installed.
7335 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7336 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7337 * differently in 4.0 and 3.x.
7338 */
7339 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7340 Assert(sz >= sizeof(VBoxHeadless_exe));
7341 strcpy(cmd, VBoxHeadless_exe);
7342
7343 Utf8Str idStr = mData->mUuid.toString();
7344 /* Leave space for "--capture" arg. */
7345 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7346 "--startvm", idStr.c_str(),
7347 "--vrde", "config",
7348 0, /* For "--capture". */
7349 0 };
7350 if (strType == "capture")
7351 {
7352 unsigned pos = RT_ELEMENTS(args) - 2;
7353 args[pos] = "--capture";
7354 }
7355 vrc = RTProcCreate(szPath, args, env,
7356#ifdef RT_OS_WINDOWS
7357 RTPROC_FLAGS_NO_WINDOW
7358#else
7359 0
7360#endif
7361 , &pid);
7362 }
7363#else /* !VBOX_WITH_HEADLESS */
7364 if (0)
7365 ;
7366#endif /* !VBOX_WITH_HEADLESS */
7367 else
7368 {
7369 RTEnvDestroy(env);
7370 return setError(E_INVALIDARG,
7371 tr("Invalid session type: '%s'"),
7372 strType.c_str());
7373 }
7374
7375 RTEnvDestroy(env);
7376
7377 if (RT_FAILURE(vrc))
7378 return setError(VBOX_E_IPRT_ERROR,
7379 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7380 mUserData->s.strName.c_str(), vrc);
7381
7382 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7383
7384 /*
7385 * Note that we don't release the lock here before calling the client,
7386 * because it doesn't need to call us back if called with a NULL argument.
7387 * Releasing the lock here is dangerous because we didn't prepare the
7388 * launch data yet, but the client we've just started may happen to be
7389 * too fast and call openSession() that will fail (because of PID, etc.),
7390 * so that the Machine will never get out of the Spawning session state.
7391 */
7392
7393 /* inform the session that it will be a remote one */
7394 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7395 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7396 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7397
7398 if (FAILED(rc))
7399 {
7400 /* restore the session state */
7401 mData->mSession.mState = SessionState_Unlocked;
7402 /* The failure may occur w/o any error info (from RPC), so provide one */
7403 return setError(VBOX_E_VM_ERROR,
7404 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7405 }
7406
7407 /* attach launch data to the machine */
7408 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7409 mData->mSession.mRemoteControls.push_back(aControl);
7410 mData->mSession.mProgress = aProgress;
7411 mData->mSession.mPID = pid;
7412 mData->mSession.mState = SessionState_Spawning;
7413 mData->mSession.mType = strType;
7414
7415 LogFlowThisFuncLeave();
7416 return S_OK;
7417}
7418
7419/**
7420 * Returns @c true if the given machine has an open direct session and returns
7421 * the session machine instance and additional session data (on some platforms)
7422 * if so.
7423 *
7424 * Note that when the method returns @c false, the arguments remain unchanged.
7425 *
7426 * @param aMachine Session machine object.
7427 * @param aControl Direct session control object (optional).
7428 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7429 *
7430 * @note locks this object for reading.
7431 */
7432#if defined(RT_OS_WINDOWS)
7433bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7434 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7435 HANDLE *aIPCSem /*= NULL*/,
7436 bool aAllowClosing /*= false*/)
7437#elif defined(RT_OS_OS2)
7438bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7439 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7440 HMTX *aIPCSem /*= NULL*/,
7441 bool aAllowClosing /*= false*/)
7442#else
7443bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7444 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7445 bool aAllowClosing /*= false*/)
7446#endif
7447{
7448 AutoLimitedCaller autoCaller(this);
7449 AssertComRCReturn(autoCaller.rc(), false);
7450
7451 /* just return false for inaccessible machines */
7452 if (autoCaller.state() != Ready)
7453 return false;
7454
7455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7456
7457 if ( mData->mSession.mState == SessionState_Locked
7458 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7459 )
7460 {
7461 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7462
7463 aMachine = mData->mSession.mMachine;
7464
7465 if (aControl != NULL)
7466 *aControl = mData->mSession.mDirectControl;
7467
7468#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7469 /* Additional session data */
7470 if (aIPCSem != NULL)
7471 *aIPCSem = aMachine->mIPCSem;
7472#endif
7473 return true;
7474 }
7475
7476 return false;
7477}
7478
7479/**
7480 * Returns @c true if the given machine has an spawning direct session and
7481 * returns and additional session data (on some platforms) if so.
7482 *
7483 * Note that when the method returns @c false, the arguments remain unchanged.
7484 *
7485 * @param aPID PID of the spawned direct session process.
7486 *
7487 * @note locks this object for reading.
7488 */
7489#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7490bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7491#else
7492bool Machine::isSessionSpawning()
7493#endif
7494{
7495 AutoLimitedCaller autoCaller(this);
7496 AssertComRCReturn(autoCaller.rc(), false);
7497
7498 /* just return false for inaccessible machines */
7499 if (autoCaller.state() != Ready)
7500 return false;
7501
7502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7503
7504 if (mData->mSession.mState == SessionState_Spawning)
7505 {
7506#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7507 /* Additional session data */
7508 if (aPID != NULL)
7509 {
7510 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7511 *aPID = mData->mSession.mPID;
7512 }
7513#endif
7514 return true;
7515 }
7516
7517 return false;
7518}
7519
7520/**
7521 * Called from the client watcher thread to check for unexpected client process
7522 * death during Session_Spawning state (e.g. before it successfully opened a
7523 * direct session).
7524 *
7525 * On Win32 and on OS/2, this method is called only when we've got the
7526 * direct client's process termination notification, so it always returns @c
7527 * true.
7528 *
7529 * On other platforms, this method returns @c true if the client process is
7530 * terminated and @c false if it's still alive.
7531 *
7532 * @note Locks this object for writing.
7533 */
7534bool Machine::checkForSpawnFailure()
7535{
7536 AutoCaller autoCaller(this);
7537 if (!autoCaller.isOk())
7538 {
7539 /* nothing to do */
7540 LogFlowThisFunc(("Already uninitialized!\n"));
7541 return true;
7542 }
7543
7544 /* VirtualBox::addProcessToReap() needs a write lock */
7545 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7546
7547 if (mData->mSession.mState != SessionState_Spawning)
7548 {
7549 /* nothing to do */
7550 LogFlowThisFunc(("Not spawning any more!\n"));
7551 return true;
7552 }
7553
7554 HRESULT rc = S_OK;
7555
7556#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7557
7558 /* the process was already unexpectedly terminated, we just need to set an
7559 * error and finalize session spawning */
7560 rc = setError(E_FAIL,
7561 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7562 getName().c_str());
7563#else
7564
7565 /* PID not yet initialized, skip check. */
7566 if (mData->mSession.mPID == NIL_RTPROCESS)
7567 return false;
7568
7569 RTPROCSTATUS status;
7570 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7571 &status);
7572
7573 if (vrc != VERR_PROCESS_RUNNING)
7574 {
7575 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7576 rc = setError(E_FAIL,
7577 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7578 getName().c_str(), status.iStatus);
7579 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7580 rc = setError(E_FAIL,
7581 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7582 getName().c_str(), status.iStatus);
7583 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7584 rc = setError(E_FAIL,
7585 tr("The virtual machine '%s' has terminated abnormally"),
7586 getName().c_str(), status.iStatus);
7587 else
7588 rc = setError(E_FAIL,
7589 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7590 getName().c_str(), rc);
7591 }
7592
7593#endif
7594
7595 if (FAILED(rc))
7596 {
7597 /* Close the remote session, remove the remote control from the list
7598 * and reset session state to Closed (@note keep the code in sync with
7599 * the relevant part in checkForSpawnFailure()). */
7600
7601 Assert(mData->mSession.mRemoteControls.size() == 1);
7602 if (mData->mSession.mRemoteControls.size() == 1)
7603 {
7604 ErrorInfoKeeper eik;
7605 mData->mSession.mRemoteControls.front()->Uninitialize();
7606 }
7607
7608 mData->mSession.mRemoteControls.clear();
7609 mData->mSession.mState = SessionState_Unlocked;
7610
7611 /* finalize the progress after setting the state */
7612 if (!mData->mSession.mProgress.isNull())
7613 {
7614 mData->mSession.mProgress->notifyComplete(rc);
7615 mData->mSession.mProgress.setNull();
7616 }
7617
7618 mParent->addProcessToReap(mData->mSession.mPID);
7619 mData->mSession.mPID = NIL_RTPROCESS;
7620
7621 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7622 return true;
7623 }
7624
7625 return false;
7626}
7627
7628/**
7629 * Checks whether the machine can be registered. If so, commits and saves
7630 * all settings.
7631 *
7632 * @note Must be called from mParent's write lock. Locks this object and
7633 * children for writing.
7634 */
7635HRESULT Machine::prepareRegister()
7636{
7637 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7638
7639 AutoLimitedCaller autoCaller(this);
7640 AssertComRCReturnRC(autoCaller.rc());
7641
7642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7643
7644 /* wait for state dependents to drop to zero */
7645 ensureNoStateDependencies();
7646
7647 if (!mData->mAccessible)
7648 return setError(VBOX_E_INVALID_OBJECT_STATE,
7649 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7650 mUserData->s.strName.c_str(),
7651 mData->mUuid.toString().c_str());
7652
7653 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7654
7655 if (mData->mRegistered)
7656 return setError(VBOX_E_INVALID_OBJECT_STATE,
7657 tr("The machine '%s' with UUID {%s} is already registered"),
7658 mUserData->s.strName.c_str(),
7659 mData->mUuid.toString().c_str());
7660
7661 HRESULT rc = S_OK;
7662
7663 // Ensure the settings are saved. If we are going to be registered and
7664 // no config file exists yet, create it by calling saveSettings() too.
7665 if ( (mData->flModifications)
7666 || (!mData->pMachineConfigFile->fileExists())
7667 )
7668 {
7669 rc = saveSettings(NULL);
7670 // no need to check whether VirtualBox.xml needs saving too since
7671 // we can't have a machine XML file rename pending
7672 if (FAILED(rc)) return rc;
7673 }
7674
7675 /* more config checking goes here */
7676
7677 if (SUCCEEDED(rc))
7678 {
7679 /* we may have had implicit modifications we want to fix on success */
7680 commit();
7681
7682 mData->mRegistered = true;
7683 }
7684 else
7685 {
7686 /* we may have had implicit modifications we want to cancel on failure*/
7687 rollback(false /* aNotify */);
7688 }
7689
7690 return rc;
7691}
7692
7693/**
7694 * Increases the number of objects dependent on the machine state or on the
7695 * registered state. Guarantees that these two states will not change at least
7696 * until #releaseStateDependency() is called.
7697 *
7698 * Depending on the @a aDepType value, additional state checks may be made.
7699 * These checks will set extended error info on failure. See
7700 * #checkStateDependency() for more info.
7701 *
7702 * If this method returns a failure, the dependency is not added and the caller
7703 * is not allowed to rely on any particular machine state or registration state
7704 * value and may return the failed result code to the upper level.
7705 *
7706 * @param aDepType Dependency type to add.
7707 * @param aState Current machine state (NULL if not interested).
7708 * @param aRegistered Current registered state (NULL if not interested).
7709 *
7710 * @note Locks this object for writing.
7711 */
7712HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7713 MachineState_T *aState /* = NULL */,
7714 BOOL *aRegistered /* = NULL */)
7715{
7716 AutoCaller autoCaller(this);
7717 AssertComRCReturnRC(autoCaller.rc());
7718
7719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7720
7721 HRESULT rc = checkStateDependency(aDepType);
7722 if (FAILED(rc)) return rc;
7723
7724 {
7725 if (mData->mMachineStateChangePending != 0)
7726 {
7727 /* ensureNoStateDependencies() is waiting for state dependencies to
7728 * drop to zero so don't add more. It may make sense to wait a bit
7729 * and retry before reporting an error (since the pending state
7730 * transition should be really quick) but let's just assert for
7731 * now to see if it ever happens on practice. */
7732
7733 AssertFailed();
7734
7735 return setError(E_ACCESSDENIED,
7736 tr("Machine state change is in progress. Please retry the operation later."));
7737 }
7738
7739 ++mData->mMachineStateDeps;
7740 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7741 }
7742
7743 if (aState)
7744 *aState = mData->mMachineState;
7745 if (aRegistered)
7746 *aRegistered = mData->mRegistered;
7747
7748 return S_OK;
7749}
7750
7751/**
7752 * Decreases the number of objects dependent on the machine state.
7753 * Must always complete the #addStateDependency() call after the state
7754 * dependency is no more necessary.
7755 */
7756void Machine::releaseStateDependency()
7757{
7758 AutoCaller autoCaller(this);
7759 AssertComRCReturnVoid(autoCaller.rc());
7760
7761 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7762
7763 /* releaseStateDependency() w/o addStateDependency()? */
7764 AssertReturnVoid(mData->mMachineStateDeps != 0);
7765 -- mData->mMachineStateDeps;
7766
7767 if (mData->mMachineStateDeps == 0)
7768 {
7769 /* inform ensureNoStateDependencies() that there are no more deps */
7770 if (mData->mMachineStateChangePending != 0)
7771 {
7772 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7773 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7774 }
7775 }
7776}
7777
7778// protected methods
7779/////////////////////////////////////////////////////////////////////////////
7780
7781/**
7782 * Performs machine state checks based on the @a aDepType value. If a check
7783 * fails, this method will set extended error info, otherwise it will return
7784 * S_OK. It is supposed, that on failure, the caller will immediately return
7785 * the return value of this method to the upper level.
7786 *
7787 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7788 *
7789 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7790 * current state of this machine object allows to change settings of the
7791 * machine (i.e. the machine is not registered, or registered but not running
7792 * and not saved). It is useful to call this method from Machine setters
7793 * before performing any change.
7794 *
7795 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7796 * as for MutableStateDep except that if the machine is saved, S_OK is also
7797 * returned. This is useful in setters which allow changing machine
7798 * properties when it is in the saved state.
7799 *
7800 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7801 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7802 * Aborted).
7803 *
7804 * @param aDepType Dependency type to check.
7805 *
7806 * @note Non Machine based classes should use #addStateDependency() and
7807 * #releaseStateDependency() methods or the smart AutoStateDependency
7808 * template.
7809 *
7810 * @note This method must be called from under this object's read or write
7811 * lock.
7812 */
7813HRESULT Machine::checkStateDependency(StateDependency aDepType)
7814{
7815 switch (aDepType)
7816 {
7817 case AnyStateDep:
7818 {
7819 break;
7820 }
7821 case MutableStateDep:
7822 {
7823 if ( mData->mRegistered
7824 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7825 || ( mData->mMachineState != MachineState_Paused
7826 && mData->mMachineState != MachineState_Running
7827 && mData->mMachineState != MachineState_Aborted
7828 && mData->mMachineState != MachineState_Teleported
7829 && mData->mMachineState != MachineState_PoweredOff
7830 )
7831 )
7832 )
7833 return setError(VBOX_E_INVALID_VM_STATE,
7834 tr("The machine is not mutable (state is %s)"),
7835 Global::stringifyMachineState(mData->mMachineState));
7836 break;
7837 }
7838 case MutableOrSavedStateDep:
7839 {
7840 if ( mData->mRegistered
7841 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7842 || ( mData->mMachineState != MachineState_Paused
7843 && mData->mMachineState != MachineState_Running
7844 && mData->mMachineState != MachineState_Aborted
7845 && mData->mMachineState != MachineState_Teleported
7846 && mData->mMachineState != MachineState_Saved
7847 && mData->mMachineState != MachineState_PoweredOff
7848 )
7849 )
7850 )
7851 return setError(VBOX_E_INVALID_VM_STATE,
7852 tr("The machine is not mutable (state is %s)"),
7853 Global::stringifyMachineState(mData->mMachineState));
7854 break;
7855 }
7856 case OfflineStateDep:
7857 {
7858 if ( mData->mRegistered
7859 && ( !isSessionMachine()
7860 || ( mData->mMachineState != MachineState_PoweredOff
7861 && mData->mMachineState != MachineState_Saved
7862 && mData->mMachineState != MachineState_Aborted
7863 && mData->mMachineState != MachineState_Teleported
7864 )
7865 )
7866 )
7867 return setError(VBOX_E_INVALID_VM_STATE,
7868 tr("The machine is not offline (state is %s)"),
7869 Global::stringifyMachineState(mData->mMachineState));
7870 break;
7871 }
7872 }
7873
7874 return S_OK;
7875}
7876
7877/**
7878 * Helper to initialize all associated child objects and allocate data
7879 * structures.
7880 *
7881 * This method must be called as a part of the object's initialization procedure
7882 * (usually done in the #init() method).
7883 *
7884 * @note Must be called only from #init() or from #registeredInit().
7885 */
7886HRESULT Machine::initDataAndChildObjects()
7887{
7888 AutoCaller autoCaller(this);
7889 AssertComRCReturnRC(autoCaller.rc());
7890 AssertComRCReturn(autoCaller.state() == InInit ||
7891 autoCaller.state() == Limited, E_FAIL);
7892
7893 AssertReturn(!mData->mAccessible, E_FAIL);
7894
7895 /* allocate data structures */
7896 mSSData.allocate();
7897 mUserData.allocate();
7898 mHWData.allocate();
7899 mMediaData.allocate();
7900 mStorageControllers.allocate();
7901
7902 /* initialize mOSTypeId */
7903 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7904
7905 /* create associated BIOS settings object */
7906 unconst(mBIOSSettings).createObject();
7907 mBIOSSettings->init(this);
7908
7909 /* create an associated VRDE object (default is disabled) */
7910 unconst(mVRDEServer).createObject();
7911 mVRDEServer->init(this);
7912
7913 /* create associated serial port objects */
7914 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7915 {
7916 unconst(mSerialPorts[slot]).createObject();
7917 mSerialPorts[slot]->init(this, slot);
7918 }
7919
7920 /* create associated parallel port objects */
7921 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7922 {
7923 unconst(mParallelPorts[slot]).createObject();
7924 mParallelPorts[slot]->init(this, slot);
7925 }
7926
7927 /* create the audio adapter object (always present, default is disabled) */
7928 unconst(mAudioAdapter).createObject();
7929 mAudioAdapter->init(this);
7930
7931 /* create the USB controller object (always present, default is disabled) */
7932 unconst(mUSBController).createObject();
7933 mUSBController->init(this);
7934
7935 /* create associated network adapter objects */
7936 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7937 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7938 {
7939 unconst(mNetworkAdapters[slot]).createObject();
7940 mNetworkAdapters[slot]->init(this, slot);
7941 }
7942
7943 /* create the bandwidth control */
7944 unconst(mBandwidthControl).createObject();
7945 mBandwidthControl->init(this);
7946
7947 return S_OK;
7948}
7949
7950/**
7951 * Helper to uninitialize all associated child objects and to free all data
7952 * structures.
7953 *
7954 * This method must be called as a part of the object's uninitialization
7955 * procedure (usually done in the #uninit() method).
7956 *
7957 * @note Must be called only from #uninit() or from #registeredInit().
7958 */
7959void Machine::uninitDataAndChildObjects()
7960{
7961 AutoCaller autoCaller(this);
7962 AssertComRCReturnVoid(autoCaller.rc());
7963 AssertComRCReturnVoid( autoCaller.state() == InUninit
7964 || autoCaller.state() == Limited);
7965
7966 /* tell all our other child objects we've been uninitialized */
7967 if (mBandwidthControl)
7968 {
7969 mBandwidthControl->uninit();
7970 unconst(mBandwidthControl).setNull();
7971 }
7972
7973 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7974 {
7975 if (mNetworkAdapters[slot])
7976 {
7977 mNetworkAdapters[slot]->uninit();
7978 unconst(mNetworkAdapters[slot]).setNull();
7979 }
7980 }
7981
7982 if (mUSBController)
7983 {
7984 mUSBController->uninit();
7985 unconst(mUSBController).setNull();
7986 }
7987
7988 if (mAudioAdapter)
7989 {
7990 mAudioAdapter->uninit();
7991 unconst(mAudioAdapter).setNull();
7992 }
7993
7994 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7995 {
7996 if (mParallelPorts[slot])
7997 {
7998 mParallelPorts[slot]->uninit();
7999 unconst(mParallelPorts[slot]).setNull();
8000 }
8001 }
8002
8003 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8004 {
8005 if (mSerialPorts[slot])
8006 {
8007 mSerialPorts[slot]->uninit();
8008 unconst(mSerialPorts[slot]).setNull();
8009 }
8010 }
8011
8012 if (mVRDEServer)
8013 {
8014 mVRDEServer->uninit();
8015 unconst(mVRDEServer).setNull();
8016 }
8017
8018 if (mBIOSSettings)
8019 {
8020 mBIOSSettings->uninit();
8021 unconst(mBIOSSettings).setNull();
8022 }
8023
8024 /* Deassociate media (only when a real Machine or a SnapshotMachine
8025 * instance is uninitialized; SessionMachine instances refer to real
8026 * Machine media). This is necessary for a clean re-initialization of
8027 * the VM after successfully re-checking the accessibility state. Note
8028 * that in case of normal Machine or SnapshotMachine uninitialization (as
8029 * a result of unregistering or deleting the snapshot), outdated media
8030 * attachments will already be uninitialized and deleted, so this
8031 * code will not affect them. */
8032 if ( !!mMediaData
8033 && (!isSessionMachine())
8034 )
8035 {
8036 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8037 it != mMediaData->mAttachments.end();
8038 ++it)
8039 {
8040 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8041 if (pMedium.isNull())
8042 continue;
8043 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8044 AssertComRC(rc);
8045 }
8046 }
8047
8048 if (!isSessionMachine() && !isSnapshotMachine())
8049 {
8050 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8051 if (mData->mFirstSnapshot)
8052 {
8053 // snapshots tree is protected by machine write lock; strictly
8054 // this isn't necessary here since we're deleting the entire
8055 // machine, but otherwise we assert in Snapshot::uninit()
8056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8057 mData->mFirstSnapshot->uninit();
8058 mData->mFirstSnapshot.setNull();
8059 }
8060
8061 mData->mCurrentSnapshot.setNull();
8062 }
8063
8064 /* free data structures (the essential mData structure is not freed here
8065 * since it may be still in use) */
8066 mMediaData.free();
8067 mStorageControllers.free();
8068 mHWData.free();
8069 mUserData.free();
8070 mSSData.free();
8071}
8072
8073/**
8074 * Returns a pointer to the Machine object for this machine that acts like a
8075 * parent for complex machine data objects such as shared folders, etc.
8076 *
8077 * For primary Machine objects and for SnapshotMachine objects, returns this
8078 * object's pointer itself. For SessionMachine objects, returns the peer
8079 * (primary) machine pointer.
8080 */
8081Machine* Machine::getMachine()
8082{
8083 if (isSessionMachine())
8084 return (Machine*)mPeer;
8085 return this;
8086}
8087
8088/**
8089 * Makes sure that there are no machine state dependents. If necessary, waits
8090 * for the number of dependents to drop to zero.
8091 *
8092 * Make sure this method is called from under this object's write lock to
8093 * guarantee that no new dependents may be added when this method returns
8094 * control to the caller.
8095 *
8096 * @note Locks this object for writing. The lock will be released while waiting
8097 * (if necessary).
8098 *
8099 * @warning To be used only in methods that change the machine state!
8100 */
8101void Machine::ensureNoStateDependencies()
8102{
8103 AssertReturnVoid(isWriteLockOnCurrentThread());
8104
8105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8106
8107 /* Wait for all state dependents if necessary */
8108 if (mData->mMachineStateDeps != 0)
8109 {
8110 /* lazy semaphore creation */
8111 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8112 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8113
8114 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8115 mData->mMachineStateDeps));
8116
8117 ++mData->mMachineStateChangePending;
8118
8119 /* reset the semaphore before waiting, the last dependent will signal
8120 * it */
8121 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8122
8123 alock.release();
8124
8125 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8126
8127 alock.acquire();
8128
8129 -- mData->mMachineStateChangePending;
8130 }
8131}
8132
8133/**
8134 * Changes the machine state and informs callbacks.
8135 *
8136 * This method is not intended to fail so it either returns S_OK or asserts (and
8137 * returns a failure).
8138 *
8139 * @note Locks this object for writing.
8140 */
8141HRESULT Machine::setMachineState(MachineState_T aMachineState)
8142{
8143 LogFlowThisFuncEnter();
8144 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8145
8146 AutoCaller autoCaller(this);
8147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8148
8149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8150
8151 /* wait for state dependents to drop to zero */
8152 ensureNoStateDependencies();
8153
8154 if (mData->mMachineState != aMachineState)
8155 {
8156 mData->mMachineState = aMachineState;
8157
8158 RTTimeNow(&mData->mLastStateChange);
8159
8160 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8161 }
8162
8163 LogFlowThisFuncLeave();
8164 return S_OK;
8165}
8166
8167/**
8168 * Searches for a shared folder with the given logical name
8169 * in the collection of shared folders.
8170 *
8171 * @param aName logical name of the shared folder
8172 * @param aSharedFolder where to return the found object
8173 * @param aSetError whether to set the error info if the folder is
8174 * not found
8175 * @return
8176 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8177 *
8178 * @note
8179 * must be called from under the object's lock!
8180 */
8181HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8182 ComObjPtr<SharedFolder> &aSharedFolder,
8183 bool aSetError /* = false */)
8184{
8185 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8186 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8187 it != mHWData->mSharedFolders.end();
8188 ++it)
8189 {
8190 SharedFolder *pSF = *it;
8191 AutoCaller autoCaller(pSF);
8192 if (pSF->getName() == aName)
8193 {
8194 aSharedFolder = pSF;
8195 rc = S_OK;
8196 break;
8197 }
8198 }
8199
8200 if (aSetError && FAILED(rc))
8201 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8202
8203 return rc;
8204}
8205
8206/**
8207 * Initializes all machine instance data from the given settings structures
8208 * from XML. The exception is the machine UUID which needs special handling
8209 * depending on the caller's use case, so the caller needs to set that herself.
8210 *
8211 * This gets called in several contexts during machine initialization:
8212 *
8213 * -- When machine XML exists on disk already and needs to be loaded into memory,
8214 * for example, from registeredInit() to load all registered machines on
8215 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8216 * attached to the machine should be part of some media registry already.
8217 *
8218 * -- During OVF import, when a machine config has been constructed from an
8219 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8220 * ensure that the media listed as attachments in the config (which have
8221 * been imported from the OVF) receive the correct registry ID.
8222 *
8223 * -- During VM cloning.
8224 *
8225 * @param config Machine settings from XML.
8226 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8227 * @return
8228 */
8229HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8230 const Guid *puuidRegistry)
8231{
8232 // copy name, description, OS type, teleporter, UTC etc.
8233 mUserData->s = config.machineUserData;
8234
8235 // look up the object by Id to check it is valid
8236 ComPtr<IGuestOSType> guestOSType;
8237 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8238 guestOSType.asOutParam());
8239 if (FAILED(rc)) return rc;
8240
8241 // stateFile (optional)
8242 if (config.strStateFile.isEmpty())
8243 mSSData->strStateFilePath.setNull();
8244 else
8245 {
8246 Utf8Str stateFilePathFull(config.strStateFile);
8247 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8248 if (RT_FAILURE(vrc))
8249 return setError(E_FAIL,
8250 tr("Invalid saved state file path '%s' (%Rrc)"),
8251 config.strStateFile.c_str(),
8252 vrc);
8253 mSSData->strStateFilePath = stateFilePathFull;
8254 }
8255
8256 // snapshot folder needs special processing so set it again
8257 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8258 if (FAILED(rc)) return rc;
8259
8260 /* Copy the extra data items (Not in any case config is already the same as
8261 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8262 * make sure the extra data map is copied). */
8263 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8264
8265 /* currentStateModified (optional, default is true) */
8266 mData->mCurrentStateModified = config.fCurrentStateModified;
8267
8268 mData->mLastStateChange = config.timeLastStateChange;
8269
8270 /*
8271 * note: all mUserData members must be assigned prior this point because
8272 * we need to commit changes in order to let mUserData be shared by all
8273 * snapshot machine instances.
8274 */
8275 mUserData.commitCopy();
8276
8277 // machine registry, if present (must be loaded before snapshots)
8278 if (config.canHaveOwnMediaRegistry())
8279 {
8280 // determine machine folder
8281 Utf8Str strMachineFolder = getSettingsFileFull();
8282 strMachineFolder.stripFilename();
8283 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8284 config.mediaRegistry,
8285 strMachineFolder);
8286 if (FAILED(rc)) return rc;
8287 }
8288
8289 /* Snapshot node (optional) */
8290 size_t cRootSnapshots;
8291 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8292 {
8293 // there must be only one root snapshot
8294 Assert(cRootSnapshots == 1);
8295
8296 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8297
8298 rc = loadSnapshot(snap,
8299 config.uuidCurrentSnapshot,
8300 NULL); // no parent == first snapshot
8301 if (FAILED(rc)) return rc;
8302 }
8303
8304 // hardware data
8305 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8306 if (FAILED(rc)) return rc;
8307
8308 // load storage controllers
8309 rc = loadStorageControllers(config.storageMachine,
8310 puuidRegistry,
8311 NULL /* puuidSnapshot */);
8312 if (FAILED(rc)) return rc;
8313
8314 /*
8315 * NOTE: the assignment below must be the last thing to do,
8316 * otherwise it will be not possible to change the settings
8317 * somewhere in the code above because all setters will be
8318 * blocked by checkStateDependency(MutableStateDep).
8319 */
8320
8321 /* set the machine state to Aborted or Saved when appropriate */
8322 if (config.fAborted)
8323 {
8324 mSSData->strStateFilePath.setNull();
8325
8326 /* no need to use setMachineState() during init() */
8327 mData->mMachineState = MachineState_Aborted;
8328 }
8329 else if (!mSSData->strStateFilePath.isEmpty())
8330 {
8331 /* no need to use setMachineState() during init() */
8332 mData->mMachineState = MachineState_Saved;
8333 }
8334
8335 // after loading settings, we are no longer different from the XML on disk
8336 mData->flModifications = 0;
8337
8338 return S_OK;
8339}
8340
8341/**
8342 * Recursively loads all snapshots starting from the given.
8343 *
8344 * @param aNode <Snapshot> node.
8345 * @param aCurSnapshotId Current snapshot ID from the settings file.
8346 * @param aParentSnapshot Parent snapshot.
8347 */
8348HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8349 const Guid &aCurSnapshotId,
8350 Snapshot *aParentSnapshot)
8351{
8352 AssertReturn(!isSnapshotMachine(), E_FAIL);
8353 AssertReturn(!isSessionMachine(), E_FAIL);
8354
8355 HRESULT rc = S_OK;
8356
8357 Utf8Str strStateFile;
8358 if (!data.strStateFile.isEmpty())
8359 {
8360 /* optional */
8361 strStateFile = data.strStateFile;
8362 int vrc = calculateFullPath(strStateFile, strStateFile);
8363 if (RT_FAILURE(vrc))
8364 return setError(E_FAIL,
8365 tr("Invalid saved state file path '%s' (%Rrc)"),
8366 strStateFile.c_str(),
8367 vrc);
8368 }
8369
8370 /* create a snapshot machine object */
8371 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8372 pSnapshotMachine.createObject();
8373 rc = pSnapshotMachine->initFromSettings(this,
8374 data.hardware,
8375 &data.debugging,
8376 &data.autostart,
8377 data.storage,
8378 data.uuid.ref(),
8379 strStateFile);
8380 if (FAILED(rc)) return rc;
8381
8382 /* create a snapshot object */
8383 ComObjPtr<Snapshot> pSnapshot;
8384 pSnapshot.createObject();
8385 /* initialize the snapshot */
8386 rc = pSnapshot->init(mParent, // VirtualBox object
8387 data.uuid,
8388 data.strName,
8389 data.strDescription,
8390 data.timestamp,
8391 pSnapshotMachine,
8392 aParentSnapshot);
8393 if (FAILED(rc)) return rc;
8394
8395 /* memorize the first snapshot if necessary */
8396 if (!mData->mFirstSnapshot)
8397 mData->mFirstSnapshot = pSnapshot;
8398
8399 /* memorize the current snapshot when appropriate */
8400 if ( !mData->mCurrentSnapshot
8401 && pSnapshot->getId() == aCurSnapshotId
8402 )
8403 mData->mCurrentSnapshot = pSnapshot;
8404
8405 // now create the children
8406 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8407 it != data.llChildSnapshots.end();
8408 ++it)
8409 {
8410 const settings::Snapshot &childData = *it;
8411 // recurse
8412 rc = loadSnapshot(childData,
8413 aCurSnapshotId,
8414 pSnapshot); // parent = the one we created above
8415 if (FAILED(rc)) return rc;
8416 }
8417
8418 return rc;
8419}
8420
8421/**
8422 * Loads settings into mHWData.
8423 *
8424 * @param data Reference to the hardware settings.
8425 * @param pDbg Pointer to the debugging settings.
8426 * @param pAutostart Pointer to the autostart settings.
8427 */
8428HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8429 const settings::Autostart *pAutostart)
8430{
8431 AssertReturn(!isSessionMachine(), E_FAIL);
8432
8433 HRESULT rc = S_OK;
8434
8435 try
8436 {
8437 /* The hardware version attribute (optional). */
8438 mHWData->mHWVersion = data.strVersion;
8439 mHWData->mHardwareUUID = data.uuid;
8440
8441 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8442 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8443 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8444 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8445 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8446 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8447 mHWData->mPAEEnabled = data.fPAE;
8448 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8449
8450 mHWData->mCPUCount = data.cCPUs;
8451 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8452 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8453
8454 // cpu
8455 if (mHWData->mCPUHotPlugEnabled)
8456 {
8457 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8458 it != data.llCpus.end();
8459 ++it)
8460 {
8461 const settings::Cpu &cpu = *it;
8462
8463 mHWData->mCPUAttached[cpu.ulId] = true;
8464 }
8465 }
8466
8467 // cpuid leafs
8468 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8469 it != data.llCpuIdLeafs.end();
8470 ++it)
8471 {
8472 const settings::CpuIdLeaf &leaf = *it;
8473
8474 switch (leaf.ulId)
8475 {
8476 case 0x0:
8477 case 0x1:
8478 case 0x2:
8479 case 0x3:
8480 case 0x4:
8481 case 0x5:
8482 case 0x6:
8483 case 0x7:
8484 case 0x8:
8485 case 0x9:
8486 case 0xA:
8487 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8488 break;
8489
8490 case 0x80000000:
8491 case 0x80000001:
8492 case 0x80000002:
8493 case 0x80000003:
8494 case 0x80000004:
8495 case 0x80000005:
8496 case 0x80000006:
8497 case 0x80000007:
8498 case 0x80000008:
8499 case 0x80000009:
8500 case 0x8000000A:
8501 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8502 break;
8503
8504 default:
8505 /* just ignore */
8506 break;
8507 }
8508 }
8509
8510 mHWData->mMemorySize = data.ulMemorySizeMB;
8511 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8512
8513 // boot order
8514 for (size_t i = 0;
8515 i < RT_ELEMENTS(mHWData->mBootOrder);
8516 i++)
8517 {
8518 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8519 if (it == data.mapBootOrder.end())
8520 mHWData->mBootOrder[i] = DeviceType_Null;
8521 else
8522 mHWData->mBootOrder[i] = it->second;
8523 }
8524
8525 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8526 mHWData->mMonitorCount = data.cMonitors;
8527 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8528 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8529 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8530 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8531 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8532 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8533 mHWData->mFirmwareType = data.firmwareType;
8534 mHWData->mPointingHIDType = data.pointingHIDType;
8535 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8536 mHWData->mChipsetType = data.chipsetType;
8537 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8538 mHWData->mHPETEnabled = data.fHPETEnabled;
8539
8540 /* VRDEServer */
8541 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8542 if (FAILED(rc)) return rc;
8543
8544 /* BIOS */
8545 rc = mBIOSSettings->loadSettings(data.biosSettings);
8546 if (FAILED(rc)) return rc;
8547
8548 // Bandwidth control (must come before network adapters)
8549 rc = mBandwidthControl->loadSettings(data.ioSettings);
8550 if (FAILED(rc)) return rc;
8551
8552 /* USB Controller */
8553 rc = mUSBController->loadSettings(data.usbController);
8554 if (FAILED(rc)) return rc;
8555
8556 // network adapters
8557 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8558 uint32_t oldCount = mNetworkAdapters.size();
8559 if (newCount > oldCount)
8560 {
8561 mNetworkAdapters.resize(newCount);
8562 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8563 {
8564 unconst(mNetworkAdapters[slot]).createObject();
8565 mNetworkAdapters[slot]->init(this, slot);
8566 }
8567 }
8568 else if (newCount < oldCount)
8569 mNetworkAdapters.resize(newCount);
8570 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8571 it != data.llNetworkAdapters.end();
8572 ++it)
8573 {
8574 const settings::NetworkAdapter &nic = *it;
8575
8576 /* slot unicity is guaranteed by XML Schema */
8577 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8578 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8579 if (FAILED(rc)) return rc;
8580 }
8581
8582 // serial ports
8583 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8584 it != data.llSerialPorts.end();
8585 ++it)
8586 {
8587 const settings::SerialPort &s = *it;
8588
8589 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8590 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8591 if (FAILED(rc)) return rc;
8592 }
8593
8594 // parallel ports (optional)
8595 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8596 it != data.llParallelPorts.end();
8597 ++it)
8598 {
8599 const settings::ParallelPort &p = *it;
8600
8601 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8602 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8603 if (FAILED(rc)) return rc;
8604 }
8605
8606 /* AudioAdapter */
8607 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8608 if (FAILED(rc)) return rc;
8609
8610 /* Shared folders */
8611 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8612 it != data.llSharedFolders.end();
8613 ++it)
8614 {
8615 const settings::SharedFolder &sf = *it;
8616
8617 ComObjPtr<SharedFolder> sharedFolder;
8618 /* Check for double entries. Not allowed! */
8619 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8620 if (SUCCEEDED(rc))
8621 return setError(VBOX_E_OBJECT_IN_USE,
8622 tr("Shared folder named '%s' already exists"),
8623 sf.strName.c_str());
8624
8625 /* Create the new shared folder. Don't break on error. This will be
8626 * reported when the machine starts. */
8627 sharedFolder.createObject();
8628 rc = sharedFolder->init(getMachine(),
8629 sf.strName,
8630 sf.strHostPath,
8631 RT_BOOL(sf.fWritable),
8632 RT_BOOL(sf.fAutoMount),
8633 false /* fFailOnError */);
8634 if (FAILED(rc)) return rc;
8635 mHWData->mSharedFolders.push_back(sharedFolder);
8636 }
8637
8638 // Clipboard
8639 mHWData->mClipboardMode = data.clipboardMode;
8640
8641 // drag'n'drop
8642 mHWData->mDragAndDropMode = data.dragAndDropMode;
8643
8644 // guest settings
8645 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8646
8647 // IO settings
8648 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8649 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8650
8651 // Host PCI devices
8652 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8653 it != data.pciAttachments.end();
8654 ++it)
8655 {
8656 const settings::HostPCIDeviceAttachment &hpda = *it;
8657 ComObjPtr<PCIDeviceAttachment> pda;
8658
8659 pda.createObject();
8660 pda->loadSettings(this, hpda);
8661 mHWData->mPCIDeviceAssignments.push_back(pda);
8662 }
8663
8664 /*
8665 * (The following isn't really real hardware, but it lives in HWData
8666 * for reasons of convenience.)
8667 */
8668
8669#ifdef VBOX_WITH_GUEST_PROPS
8670 /* Guest properties (optional) */
8671 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8672 it != data.llGuestProperties.end();
8673 ++it)
8674 {
8675 const settings::GuestProperty &prop = *it;
8676 uint32_t fFlags = guestProp::NILFLAG;
8677 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8678 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8679 mHWData->mGuestProperties[prop.strName] = property;
8680 }
8681
8682 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8683#endif /* VBOX_WITH_GUEST_PROPS defined */
8684
8685 rc = loadDebugging(pDbg);
8686 if (FAILED(rc))
8687 return rc;
8688
8689 mHWData->mAutostart = *pAutostart;
8690 }
8691 catch(std::bad_alloc &)
8692 {
8693 return E_OUTOFMEMORY;
8694 }
8695
8696 AssertComRC(rc);
8697 return rc;
8698}
8699
8700/**
8701 * Called from Machine::loadHardware() to load the debugging settings of the
8702 * machine.
8703 *
8704 * @param pDbg Pointer to the settings.
8705 */
8706HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8707{
8708 mHWData->mDebugging = *pDbg;
8709 /* no more processing currently required, this will probably change. */
8710 return S_OK;
8711}
8712
8713/**
8714 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8715 *
8716 * @param data
8717 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8718 * @param puuidSnapshot
8719 * @return
8720 */
8721HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8722 const Guid *puuidRegistry,
8723 const Guid *puuidSnapshot)
8724{
8725 AssertReturn(!isSessionMachine(), E_FAIL);
8726
8727 HRESULT rc = S_OK;
8728
8729 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8730 it != data.llStorageControllers.end();
8731 ++it)
8732 {
8733 const settings::StorageController &ctlData = *it;
8734
8735 ComObjPtr<StorageController> pCtl;
8736 /* Try to find one with the name first. */
8737 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8738 if (SUCCEEDED(rc))
8739 return setError(VBOX_E_OBJECT_IN_USE,
8740 tr("Storage controller named '%s' already exists"),
8741 ctlData.strName.c_str());
8742
8743 pCtl.createObject();
8744 rc = pCtl->init(this,
8745 ctlData.strName,
8746 ctlData.storageBus,
8747 ctlData.ulInstance,
8748 ctlData.fBootable);
8749 if (FAILED(rc)) return rc;
8750
8751 mStorageControllers->push_back(pCtl);
8752
8753 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8754 if (FAILED(rc)) return rc;
8755
8756 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8757 if (FAILED(rc)) return rc;
8758
8759 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8760 if (FAILED(rc)) return rc;
8761
8762 /* Set IDE emulation settings (only for AHCI controller). */
8763 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8764 {
8765 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8766 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8767 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8768 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8769 )
8770 return rc;
8771 }
8772
8773 /* Load the attached devices now. */
8774 rc = loadStorageDevices(pCtl,
8775 ctlData,
8776 puuidRegistry,
8777 puuidSnapshot);
8778 if (FAILED(rc)) return rc;
8779 }
8780
8781 return S_OK;
8782}
8783
8784/**
8785 * Called from loadStorageControllers for a controller's devices.
8786 *
8787 * @param aStorageController
8788 * @param data
8789 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8790 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8791 * @return
8792 */
8793HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8794 const settings::StorageController &data,
8795 const Guid *puuidRegistry,
8796 const Guid *puuidSnapshot)
8797{
8798 HRESULT rc = S_OK;
8799
8800 /* paranoia: detect duplicate attachments */
8801 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8802 it != data.llAttachedDevices.end();
8803 ++it)
8804 {
8805 const settings::AttachedDevice &ad = *it;
8806
8807 for (settings::AttachedDevicesList::const_iterator it2 = it;
8808 it2 != data.llAttachedDevices.end();
8809 ++it2)
8810 {
8811 if (it == it2)
8812 continue;
8813
8814 const settings::AttachedDevice &ad2 = *it2;
8815
8816 if ( ad.lPort == ad2.lPort
8817 && ad.lDevice == ad2.lDevice)
8818 {
8819 return setError(E_FAIL,
8820 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8821 aStorageController->getName().c_str(),
8822 ad.lPort,
8823 ad.lDevice,
8824 mUserData->s.strName.c_str());
8825 }
8826 }
8827 }
8828
8829 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8830 it != data.llAttachedDevices.end();
8831 ++it)
8832 {
8833 const settings::AttachedDevice &dev = *it;
8834 ComObjPtr<Medium> medium;
8835
8836 switch (dev.deviceType)
8837 {
8838 case DeviceType_Floppy:
8839 case DeviceType_DVD:
8840 if (dev.strHostDriveSrc.isNotEmpty())
8841 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8842 else
8843 rc = mParent->findRemoveableMedium(dev.deviceType,
8844 dev.uuid,
8845 false /* fRefresh */,
8846 false /* aSetError */,
8847 medium);
8848 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8849 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8850 rc = S_OK;
8851 break;
8852
8853 case DeviceType_HardDisk:
8854 {
8855 /* find a hard disk by UUID */
8856 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8857 if (FAILED(rc))
8858 {
8859 if (isSnapshotMachine())
8860 {
8861 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8862 // so the user knows that the bad disk is in a snapshot somewhere
8863 com::ErrorInfo info;
8864 return setError(E_FAIL,
8865 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8866 puuidSnapshot->raw(),
8867 info.getText().raw());
8868 }
8869 else
8870 return rc;
8871 }
8872
8873 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8874
8875 if (medium->getType() == MediumType_Immutable)
8876 {
8877 if (isSnapshotMachine())
8878 return setError(E_FAIL,
8879 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8880 "of the virtual machine '%s' ('%s')"),
8881 medium->getLocationFull().c_str(),
8882 dev.uuid.raw(),
8883 puuidSnapshot->raw(),
8884 mUserData->s.strName.c_str(),
8885 mData->m_strConfigFileFull.c_str());
8886
8887 return setError(E_FAIL,
8888 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8889 medium->getLocationFull().c_str(),
8890 dev.uuid.raw(),
8891 mUserData->s.strName.c_str(),
8892 mData->m_strConfigFileFull.c_str());
8893 }
8894
8895 if (medium->getType() == MediumType_MultiAttach)
8896 {
8897 if (isSnapshotMachine())
8898 return setError(E_FAIL,
8899 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8900 "of the virtual machine '%s' ('%s')"),
8901 medium->getLocationFull().c_str(),
8902 dev.uuid.raw(),
8903 puuidSnapshot->raw(),
8904 mUserData->s.strName.c_str(),
8905 mData->m_strConfigFileFull.c_str());
8906
8907 return setError(E_FAIL,
8908 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8909 medium->getLocationFull().c_str(),
8910 dev.uuid.raw(),
8911 mUserData->s.strName.c_str(),
8912 mData->m_strConfigFileFull.c_str());
8913 }
8914
8915 if ( !isSnapshotMachine()
8916 && medium->getChildren().size() != 0
8917 )
8918 return setError(E_FAIL,
8919 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8920 "because it has %d differencing child hard disks"),
8921 medium->getLocationFull().c_str(),
8922 dev.uuid.raw(),
8923 mUserData->s.strName.c_str(),
8924 mData->m_strConfigFileFull.c_str(),
8925 medium->getChildren().size());
8926
8927 if (findAttachment(mMediaData->mAttachments,
8928 medium))
8929 return setError(E_FAIL,
8930 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8931 medium->getLocationFull().c_str(),
8932 dev.uuid.raw(),
8933 mUserData->s.strName.c_str(),
8934 mData->m_strConfigFileFull.c_str());
8935
8936 break;
8937 }
8938
8939 default:
8940 return setError(E_FAIL,
8941 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8942 medium->getLocationFull().c_str(),
8943 mUserData->s.strName.c_str(),
8944 mData->m_strConfigFileFull.c_str());
8945 }
8946
8947 if (FAILED(rc))
8948 break;
8949
8950 /* Bandwidth groups are loaded at this point. */
8951 ComObjPtr<BandwidthGroup> pBwGroup;
8952
8953 if (!dev.strBwGroup.isEmpty())
8954 {
8955 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8956 if (FAILED(rc))
8957 return setError(E_FAIL,
8958 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8959 medium->getLocationFull().c_str(),
8960 dev.strBwGroup.c_str(),
8961 mUserData->s.strName.c_str(),
8962 mData->m_strConfigFileFull.c_str());
8963 pBwGroup->reference();
8964 }
8965
8966 const Bstr controllerName = aStorageController->getName();
8967 ComObjPtr<MediumAttachment> pAttachment;
8968 pAttachment.createObject();
8969 rc = pAttachment->init(this,
8970 medium,
8971 controllerName,
8972 dev.lPort,
8973 dev.lDevice,
8974 dev.deviceType,
8975 false,
8976 dev.fPassThrough,
8977 dev.fTempEject,
8978 dev.fNonRotational,
8979 dev.fDiscard,
8980 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8981 if (FAILED(rc)) break;
8982
8983 /* associate the medium with this machine and snapshot */
8984 if (!medium.isNull())
8985 {
8986 AutoCaller medCaller(medium);
8987 if (FAILED(medCaller.rc())) return medCaller.rc();
8988 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8989
8990 if (isSnapshotMachine())
8991 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8992 else
8993 rc = medium->addBackReference(mData->mUuid);
8994 /* If the medium->addBackReference fails it sets an appropriate
8995 * error message, so no need to do any guesswork here. */
8996
8997 if (puuidRegistry)
8998 // caller wants registry ID to be set on all attached media (OVF import case)
8999 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9000 }
9001
9002 if (FAILED(rc))
9003 break;
9004
9005 /* back up mMediaData to let registeredInit() properly rollback on failure
9006 * (= limited accessibility) */
9007 setModified(IsModified_Storage);
9008 mMediaData.backup();
9009 mMediaData->mAttachments.push_back(pAttachment);
9010 }
9011
9012 return rc;
9013}
9014
9015/**
9016 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9017 *
9018 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9019 * @param aSnapshot where to return the found snapshot
9020 * @param aSetError true to set extended error info on failure
9021 */
9022HRESULT Machine::findSnapshotById(const Guid &aId,
9023 ComObjPtr<Snapshot> &aSnapshot,
9024 bool aSetError /* = false */)
9025{
9026 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9027
9028 if (!mData->mFirstSnapshot)
9029 {
9030 if (aSetError)
9031 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9032 return E_FAIL;
9033 }
9034
9035 if (aId.isZero())
9036 aSnapshot = mData->mFirstSnapshot;
9037 else
9038 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9039
9040 if (!aSnapshot)
9041 {
9042 if (aSetError)
9043 return setError(E_FAIL,
9044 tr("Could not find a snapshot with UUID {%s}"),
9045 aId.toString().c_str());
9046 return E_FAIL;
9047 }
9048
9049 return S_OK;
9050}
9051
9052/**
9053 * Returns the snapshot with the given name or fails of no such snapshot.
9054 *
9055 * @param aName snapshot name to find
9056 * @param aSnapshot where to return the found snapshot
9057 * @param aSetError true to set extended error info on failure
9058 */
9059HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9060 ComObjPtr<Snapshot> &aSnapshot,
9061 bool aSetError /* = false */)
9062{
9063 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9064
9065 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9066
9067 if (!mData->mFirstSnapshot)
9068 {
9069 if (aSetError)
9070 return setError(VBOX_E_OBJECT_NOT_FOUND,
9071 tr("This machine does not have any snapshots"));
9072 return VBOX_E_OBJECT_NOT_FOUND;
9073 }
9074
9075 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9076
9077 if (!aSnapshot)
9078 {
9079 if (aSetError)
9080 return setError(VBOX_E_OBJECT_NOT_FOUND,
9081 tr("Could not find a snapshot named '%s'"), strName.c_str());
9082 return VBOX_E_OBJECT_NOT_FOUND;
9083 }
9084
9085 return S_OK;
9086}
9087
9088/**
9089 * Returns a storage controller object with the given name.
9090 *
9091 * @param aName storage controller name to find
9092 * @param aStorageController where to return the found storage controller
9093 * @param aSetError true to set extended error info on failure
9094 */
9095HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9096 ComObjPtr<StorageController> &aStorageController,
9097 bool aSetError /* = false */)
9098{
9099 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9100
9101 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9102 it != mStorageControllers->end();
9103 ++it)
9104 {
9105 if ((*it)->getName() == aName)
9106 {
9107 aStorageController = (*it);
9108 return S_OK;
9109 }
9110 }
9111
9112 if (aSetError)
9113 return setError(VBOX_E_OBJECT_NOT_FOUND,
9114 tr("Could not find a storage controller named '%s'"),
9115 aName.c_str());
9116 return VBOX_E_OBJECT_NOT_FOUND;
9117}
9118
9119HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9120 MediaData::AttachmentList &atts)
9121{
9122 AutoCaller autoCaller(this);
9123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9124
9125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9126
9127 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9128 it != mMediaData->mAttachments.end();
9129 ++it)
9130 {
9131 const ComObjPtr<MediumAttachment> &pAtt = *it;
9132
9133 // should never happen, but deal with NULL pointers in the list.
9134 AssertStmt(!pAtt.isNull(), continue);
9135
9136 // getControllerName() needs caller+read lock
9137 AutoCaller autoAttCaller(pAtt);
9138 if (FAILED(autoAttCaller.rc()))
9139 {
9140 atts.clear();
9141 return autoAttCaller.rc();
9142 }
9143 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9144
9145 if (pAtt->getControllerName() == aName)
9146 atts.push_back(pAtt);
9147 }
9148
9149 return S_OK;
9150}
9151
9152/**
9153 * Helper for #saveSettings. Cares about renaming the settings directory and
9154 * file if the machine name was changed and about creating a new settings file
9155 * if this is a new machine.
9156 *
9157 * @note Must be never called directly but only from #saveSettings().
9158 */
9159HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9160{
9161 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9162
9163 HRESULT rc = S_OK;
9164
9165 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9166
9167 /// @todo need to handle primary group change, too
9168
9169 /* attempt to rename the settings file if machine name is changed */
9170 if ( mUserData->s.fNameSync
9171 && mUserData.isBackedUp()
9172 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9173 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9174 )
9175 {
9176 bool dirRenamed = false;
9177 bool fileRenamed = false;
9178
9179 Utf8Str configFile, newConfigFile;
9180 Utf8Str configFilePrev, newConfigFilePrev;
9181 Utf8Str configDir, newConfigDir;
9182
9183 do
9184 {
9185 int vrc = VINF_SUCCESS;
9186
9187 Utf8Str name = mUserData.backedUpData()->s.strName;
9188 Utf8Str newName = mUserData->s.strName;
9189 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9190 if (group == "/")
9191 group.setNull();
9192 Utf8Str newGroup = mUserData->s.llGroups.front();
9193 if (newGroup == "/")
9194 newGroup.setNull();
9195
9196 configFile = mData->m_strConfigFileFull;
9197
9198 /* first, rename the directory if it matches the group and machine name */
9199 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9200 group.c_str(), RTPATH_DELIMITER, name.c_str());
9201 /** @todo hack, make somehow use of ComposeMachineFilename */
9202 if (mUserData->s.fDirectoryIncludesUUID)
9203 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9204 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9205 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9206 /** @todo hack, make somehow use of ComposeMachineFilename */
9207 if (mUserData->s.fDirectoryIncludesUUID)
9208 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9209 configDir = configFile;
9210 configDir.stripFilename();
9211 newConfigDir = configDir;
9212 if ( configDir.length() >= groupPlusName.length()
9213 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9214 {
9215 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9216 Utf8Str newConfigBaseDir(newConfigDir);
9217 newConfigDir.append(newGroupPlusName);
9218 /* consistency: use \ if appropriate on the platform */
9219 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9220 /* new dir and old dir cannot be equal here because of 'if'
9221 * above and because name != newName */
9222 Assert(configDir != newConfigDir);
9223 if (!fSettingsFileIsNew)
9224 {
9225 /* perform real rename only if the machine is not new */
9226 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9227 if ( vrc == VERR_FILE_NOT_FOUND
9228 || vrc == VERR_PATH_NOT_FOUND)
9229 {
9230 /* create the parent directory, then retry renaming */
9231 Utf8Str parent(newConfigDir);
9232 parent.stripFilename();
9233 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9234 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9235 }
9236 if (RT_FAILURE(vrc))
9237 {
9238 rc = setError(E_FAIL,
9239 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9240 configDir.c_str(),
9241 newConfigDir.c_str(),
9242 vrc);
9243 break;
9244 }
9245 /* delete subdirectories which are no longer needed */
9246 Utf8Str dir(configDir);
9247 dir.stripFilename();
9248 while (dir != newConfigBaseDir && dir != ".")
9249 {
9250 vrc = RTDirRemove(dir.c_str());
9251 if (RT_FAILURE(vrc))
9252 break;
9253 dir.stripFilename();
9254 }
9255 dirRenamed = true;
9256 }
9257 }
9258
9259 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9260 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9261
9262 /* then try to rename the settings file itself */
9263 if (newConfigFile != configFile)
9264 {
9265 /* get the path to old settings file in renamed directory */
9266 configFile = Utf8StrFmt("%s%c%s",
9267 newConfigDir.c_str(),
9268 RTPATH_DELIMITER,
9269 RTPathFilename(configFile.c_str()));
9270 if (!fSettingsFileIsNew)
9271 {
9272 /* perform real rename only if the machine is not new */
9273 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9274 if (RT_FAILURE(vrc))
9275 {
9276 rc = setError(E_FAIL,
9277 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9278 configFile.c_str(),
9279 newConfigFile.c_str(),
9280 vrc);
9281 break;
9282 }
9283 fileRenamed = true;
9284 configFilePrev = configFile;
9285 configFilePrev += "-prev";
9286 newConfigFilePrev = newConfigFile;
9287 newConfigFilePrev += "-prev";
9288 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9289 }
9290 }
9291
9292 // update m_strConfigFileFull amd mConfigFile
9293 mData->m_strConfigFileFull = newConfigFile;
9294 // compute the relative path too
9295 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9296
9297 // store the old and new so that VirtualBox::saveSettings() can update
9298 // the media registry
9299 if ( mData->mRegistered
9300 && configDir != newConfigDir)
9301 {
9302 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9303
9304 if (pfNeedsGlobalSaveSettings)
9305 *pfNeedsGlobalSaveSettings = true;
9306 }
9307
9308 // in the saved state file path, replace the old directory with the new directory
9309 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9310 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9311
9312 // and do the same thing for the saved state file paths of all the online snapshots
9313 if (mData->mFirstSnapshot)
9314 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9315 newConfigDir.c_str());
9316 }
9317 while (0);
9318
9319 if (FAILED(rc))
9320 {
9321 /* silently try to rename everything back */
9322 if (fileRenamed)
9323 {
9324 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9325 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9326 }
9327 if (dirRenamed)
9328 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9329 }
9330
9331 if (FAILED(rc)) return rc;
9332 }
9333
9334 if (fSettingsFileIsNew)
9335 {
9336 /* create a virgin config file */
9337 int vrc = VINF_SUCCESS;
9338
9339 /* ensure the settings directory exists */
9340 Utf8Str path(mData->m_strConfigFileFull);
9341 path.stripFilename();
9342 if (!RTDirExists(path.c_str()))
9343 {
9344 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9345 if (RT_FAILURE(vrc))
9346 {
9347 return setError(E_FAIL,
9348 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9349 path.c_str(),
9350 vrc);
9351 }
9352 }
9353
9354 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9355 path = Utf8Str(mData->m_strConfigFileFull);
9356 RTFILE f = NIL_RTFILE;
9357 vrc = RTFileOpen(&f, path.c_str(),
9358 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9359 if (RT_FAILURE(vrc))
9360 return setError(E_FAIL,
9361 tr("Could not create the settings file '%s' (%Rrc)"),
9362 path.c_str(),
9363 vrc);
9364 RTFileClose(f);
9365 }
9366
9367 return rc;
9368}
9369
9370/**
9371 * Saves and commits machine data, user data and hardware data.
9372 *
9373 * Note that on failure, the data remains uncommitted.
9374 *
9375 * @a aFlags may combine the following flags:
9376 *
9377 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9378 * Used when saving settings after an operation that makes them 100%
9379 * correspond to the settings from the current snapshot.
9380 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9381 * #isReallyModified() returns false. This is necessary for cases when we
9382 * change machine data directly, not through the backup()/commit() mechanism.
9383 * - SaveS_Force: settings will be saved without doing a deep compare of the
9384 * settings structures. This is used when this is called because snapshots
9385 * have changed to avoid the overhead of the deep compare.
9386 *
9387 * @note Must be called from under this object's write lock. Locks children for
9388 * writing.
9389 *
9390 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9391 * initialized to false and that will be set to true by this function if
9392 * the caller must invoke VirtualBox::saveSettings() because the global
9393 * settings have changed. This will happen if a machine rename has been
9394 * saved and the global machine and media registries will therefore need
9395 * updating.
9396 */
9397HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9398 int aFlags /*= 0*/)
9399{
9400 LogFlowThisFuncEnter();
9401
9402 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9403
9404 /* make sure child objects are unable to modify the settings while we are
9405 * saving them */
9406 ensureNoStateDependencies();
9407
9408 AssertReturn(!isSnapshotMachine(),
9409 E_FAIL);
9410
9411 HRESULT rc = S_OK;
9412 bool fNeedsWrite = false;
9413
9414 /* First, prepare to save settings. It will care about renaming the
9415 * settings directory and file if the machine name was changed and about
9416 * creating a new settings file if this is a new machine. */
9417 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9418 if (FAILED(rc)) return rc;
9419
9420 // keep a pointer to the current settings structures
9421 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9422 settings::MachineConfigFile *pNewConfig = NULL;
9423
9424 try
9425 {
9426 // make a fresh one to have everyone write stuff into
9427 pNewConfig = new settings::MachineConfigFile(NULL);
9428 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9429
9430 // now go and copy all the settings data from COM to the settings structures
9431 // (this calles saveSettings() on all the COM objects in the machine)
9432 copyMachineDataToSettings(*pNewConfig);
9433
9434 if (aFlags & SaveS_ResetCurStateModified)
9435 {
9436 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9437 mData->mCurrentStateModified = FALSE;
9438 fNeedsWrite = true; // always, no need to compare
9439 }
9440 else if (aFlags & SaveS_Force)
9441 {
9442 fNeedsWrite = true; // always, no need to compare
9443 }
9444 else
9445 {
9446 if (!mData->mCurrentStateModified)
9447 {
9448 // do a deep compare of the settings that we just saved with the settings
9449 // previously stored in the config file; this invokes MachineConfigFile::operator==
9450 // which does a deep compare of all the settings, which is expensive but less expensive
9451 // than writing out XML in vain
9452 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9453
9454 // could still be modified if any settings changed
9455 mData->mCurrentStateModified = fAnySettingsChanged;
9456
9457 fNeedsWrite = fAnySettingsChanged;
9458 }
9459 else
9460 fNeedsWrite = true;
9461 }
9462
9463 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9464
9465 if (fNeedsWrite)
9466 // now spit it all out!
9467 pNewConfig->write(mData->m_strConfigFileFull);
9468
9469 mData->pMachineConfigFile = pNewConfig;
9470 delete pOldConfig;
9471 commit();
9472
9473 // after saving settings, we are no longer different from the XML on disk
9474 mData->flModifications = 0;
9475 }
9476 catch (HRESULT err)
9477 {
9478 // we assume that error info is set by the thrower
9479 rc = err;
9480
9481 // restore old config
9482 delete pNewConfig;
9483 mData->pMachineConfigFile = pOldConfig;
9484 }
9485 catch (...)
9486 {
9487 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9488 }
9489
9490 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9491 {
9492 /* Fire the data change event, even on failure (since we've already
9493 * committed all data). This is done only for SessionMachines because
9494 * mutable Machine instances are always not registered (i.e. private
9495 * to the client process that creates them) and thus don't need to
9496 * inform callbacks. */
9497 if (isSessionMachine())
9498 mParent->onMachineDataChange(mData->mUuid);
9499 }
9500
9501 LogFlowThisFunc(("rc=%08X\n", rc));
9502 LogFlowThisFuncLeave();
9503 return rc;
9504}
9505
9506/**
9507 * Implementation for saving the machine settings into the given
9508 * settings::MachineConfigFile instance. This copies machine extradata
9509 * from the previous machine config file in the instance data, if any.
9510 *
9511 * This gets called from two locations:
9512 *
9513 * -- Machine::saveSettings(), during the regular XML writing;
9514 *
9515 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9516 * exported to OVF and we write the VirtualBox proprietary XML
9517 * into a <vbox:Machine> tag.
9518 *
9519 * This routine fills all the fields in there, including snapshots, *except*
9520 * for the following:
9521 *
9522 * -- fCurrentStateModified. There is some special logic associated with that.
9523 *
9524 * The caller can then call MachineConfigFile::write() or do something else
9525 * with it.
9526 *
9527 * Caller must hold the machine lock!
9528 *
9529 * This throws XML errors and HRESULT, so the caller must have a catch block!
9530 */
9531void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9532{
9533 // deep copy extradata
9534 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9535
9536 config.uuid = mData->mUuid;
9537
9538 // copy name, description, OS type, teleport, UTC etc.
9539 config.machineUserData = mUserData->s;
9540
9541 if ( mData->mMachineState == MachineState_Saved
9542 || mData->mMachineState == MachineState_Restoring
9543 // when deleting a snapshot we may or may not have a saved state in the current state,
9544 // so let's not assert here please
9545 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9546 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9547 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9548 && (!mSSData->strStateFilePath.isEmpty())
9549 )
9550 )
9551 {
9552 Assert(!mSSData->strStateFilePath.isEmpty());
9553 /* try to make the file name relative to the settings file dir */
9554 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9555 }
9556 else
9557 {
9558 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9559 config.strStateFile.setNull();
9560 }
9561
9562 if (mData->mCurrentSnapshot)
9563 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9564 else
9565 config.uuidCurrentSnapshot.clear();
9566
9567 config.timeLastStateChange = mData->mLastStateChange;
9568 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9569 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9570
9571 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9572 if (FAILED(rc)) throw rc;
9573
9574 rc = saveStorageControllers(config.storageMachine);
9575 if (FAILED(rc)) throw rc;
9576
9577 // save machine's media registry if this is VirtualBox 4.0 or later
9578 if (config.canHaveOwnMediaRegistry())
9579 {
9580 // determine machine folder
9581 Utf8Str strMachineFolder = getSettingsFileFull();
9582 strMachineFolder.stripFilename();
9583 mParent->saveMediaRegistry(config.mediaRegistry,
9584 getId(), // only media with registry ID == machine UUID
9585 strMachineFolder);
9586 // this throws HRESULT
9587 }
9588
9589 // save snapshots
9590 rc = saveAllSnapshots(config);
9591 if (FAILED(rc)) throw rc;
9592}
9593
9594/**
9595 * Saves all snapshots of the machine into the given machine config file. Called
9596 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9597 * @param config
9598 * @return
9599 */
9600HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9601{
9602 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9603
9604 HRESULT rc = S_OK;
9605
9606 try
9607 {
9608 config.llFirstSnapshot.clear();
9609
9610 if (mData->mFirstSnapshot)
9611 {
9612 settings::Snapshot snapNew;
9613 config.llFirstSnapshot.push_back(snapNew);
9614
9615 // get reference to the fresh copy of the snapshot on the list and
9616 // work on that copy directly to avoid excessive copying later
9617 settings::Snapshot &snap = config.llFirstSnapshot.front();
9618
9619 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9620 if (FAILED(rc)) throw rc;
9621 }
9622
9623// if (mType == IsSessionMachine)
9624// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9625
9626 }
9627 catch (HRESULT err)
9628 {
9629 /* we assume that error info is set by the thrower */
9630 rc = err;
9631 }
9632 catch (...)
9633 {
9634 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9635 }
9636
9637 return rc;
9638}
9639
9640/**
9641 * Saves the VM hardware configuration. It is assumed that the
9642 * given node is empty.
9643 *
9644 * @param data Reference to the settings object for the hardware config.
9645 * @param pDbg Pointer to the settings object for the debugging config
9646 * which happens to live in mHWData.
9647 * @param pAutostart Pointer to the settings object for the autostart config
9648 * which happens to live in mHWData.
9649 */
9650HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9651 settings::Autostart *pAutostart)
9652{
9653 HRESULT rc = S_OK;
9654
9655 try
9656 {
9657 /* The hardware version attribute (optional).
9658 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9659 if ( mHWData->mHWVersion == "1"
9660 && mSSData->strStateFilePath.isEmpty()
9661 )
9662 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. */
9663
9664 data.strVersion = mHWData->mHWVersion;
9665 data.uuid = mHWData->mHardwareUUID;
9666
9667 // CPU
9668 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9669 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9670 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9671 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9672 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9673 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9674 data.fPAE = !!mHWData->mPAEEnabled;
9675 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9676
9677 /* Standard and Extended CPUID leafs. */
9678 data.llCpuIdLeafs.clear();
9679 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9680 {
9681 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9682 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9683 }
9684 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9685 {
9686 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9687 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9688 }
9689
9690 data.cCPUs = mHWData->mCPUCount;
9691 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9692 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9693
9694 data.llCpus.clear();
9695 if (data.fCpuHotPlug)
9696 {
9697 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9698 {
9699 if (mHWData->mCPUAttached[idx])
9700 {
9701 settings::Cpu cpu;
9702 cpu.ulId = idx;
9703 data.llCpus.push_back(cpu);
9704 }
9705 }
9706 }
9707
9708 // memory
9709 data.ulMemorySizeMB = mHWData->mMemorySize;
9710 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9711
9712 // firmware
9713 data.firmwareType = mHWData->mFirmwareType;
9714
9715 // HID
9716 data.pointingHIDType = mHWData->mPointingHIDType;
9717 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9718
9719 // chipset
9720 data.chipsetType = mHWData->mChipsetType;
9721
9722 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9723
9724 // HPET
9725 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9726
9727 // boot order
9728 data.mapBootOrder.clear();
9729 for (size_t i = 0;
9730 i < RT_ELEMENTS(mHWData->mBootOrder);
9731 ++i)
9732 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9733
9734 // display
9735 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9736 data.cMonitors = mHWData->mMonitorCount;
9737 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9738 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9739 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9740 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9741 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9742 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9743
9744 /* VRDEServer settings (optional) */
9745 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9746 if (FAILED(rc)) throw rc;
9747
9748 /* BIOS (required) */
9749 rc = mBIOSSettings->saveSettings(data.biosSettings);
9750 if (FAILED(rc)) throw rc;
9751
9752 /* USB Controller (required) */
9753 rc = mUSBController->saveSettings(data.usbController);
9754 if (FAILED(rc)) throw rc;
9755
9756 /* Network adapters (required) */
9757 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9758 data.llNetworkAdapters.clear();
9759 /* Write out only the nominal number of network adapters for this
9760 * chipset type. Since Machine::commit() hasn't been called there
9761 * may be extra NIC settings in the vector. */
9762 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9763 {
9764 settings::NetworkAdapter nic;
9765 nic.ulSlot = slot;
9766 /* paranoia check... must not be NULL, but must not crash either. */
9767 if (mNetworkAdapters[slot])
9768 {
9769 rc = mNetworkAdapters[slot]->saveSettings(nic);
9770 if (FAILED(rc)) throw rc;
9771
9772 data.llNetworkAdapters.push_back(nic);
9773 }
9774 }
9775
9776 /* Serial ports */
9777 data.llSerialPorts.clear();
9778 for (ULONG slot = 0;
9779 slot < RT_ELEMENTS(mSerialPorts);
9780 ++slot)
9781 {
9782 settings::SerialPort s;
9783 s.ulSlot = slot;
9784 rc = mSerialPorts[slot]->saveSettings(s);
9785 if (FAILED(rc)) return rc;
9786
9787 data.llSerialPorts.push_back(s);
9788 }
9789
9790 /* Parallel ports */
9791 data.llParallelPorts.clear();
9792 for (ULONG slot = 0;
9793 slot < RT_ELEMENTS(mParallelPorts);
9794 ++slot)
9795 {
9796 settings::ParallelPort p;
9797 p.ulSlot = slot;
9798 rc = mParallelPorts[slot]->saveSettings(p);
9799 if (FAILED(rc)) return rc;
9800
9801 data.llParallelPorts.push_back(p);
9802 }
9803
9804 /* Audio adapter */
9805 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9806 if (FAILED(rc)) return rc;
9807
9808 /* Shared folders */
9809 data.llSharedFolders.clear();
9810 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9811 it != mHWData->mSharedFolders.end();
9812 ++it)
9813 {
9814 SharedFolder *pSF = *it;
9815 AutoCaller sfCaller(pSF);
9816 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9817 settings::SharedFolder sf;
9818 sf.strName = pSF->getName();
9819 sf.strHostPath = pSF->getHostPath();
9820 sf.fWritable = !!pSF->isWritable();
9821 sf.fAutoMount = !!pSF->isAutoMounted();
9822
9823 data.llSharedFolders.push_back(sf);
9824 }
9825
9826 // clipboard
9827 data.clipboardMode = mHWData->mClipboardMode;
9828
9829 // drag'n'drop
9830 data.dragAndDropMode = mHWData->mDragAndDropMode;
9831
9832 /* Guest */
9833 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9834
9835 // IO settings
9836 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9837 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9838
9839 /* BandwidthControl (required) */
9840 rc = mBandwidthControl->saveSettings(data.ioSettings);
9841 if (FAILED(rc)) throw rc;
9842
9843 /* Host PCI devices */
9844 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9845 it != mHWData->mPCIDeviceAssignments.end();
9846 ++it)
9847 {
9848 ComObjPtr<PCIDeviceAttachment> pda = *it;
9849 settings::HostPCIDeviceAttachment hpda;
9850
9851 rc = pda->saveSettings(hpda);
9852 if (FAILED(rc)) throw rc;
9853
9854 data.pciAttachments.push_back(hpda);
9855 }
9856
9857
9858 // guest properties
9859 data.llGuestProperties.clear();
9860#ifdef VBOX_WITH_GUEST_PROPS
9861 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
9862 it != mHWData->mGuestProperties.end();
9863 ++it)
9864 {
9865 HWData::GuestProperty property = it->second;
9866
9867 /* Remove transient guest properties at shutdown unless we
9868 * are saving state */
9869 if ( ( mData->mMachineState == MachineState_PoweredOff
9870 || mData->mMachineState == MachineState_Aborted
9871 || mData->mMachineState == MachineState_Teleported)
9872 && ( property.mFlags & guestProp::TRANSIENT
9873 || property.mFlags & guestProp::TRANSRESET))
9874 continue;
9875 settings::GuestProperty prop;
9876 prop.strName = it->first;
9877 prop.strValue = property.strValue;
9878 prop.timestamp = property.mTimestamp;
9879 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9880 guestProp::writeFlags(property.mFlags, szFlags);
9881 prop.strFlags = szFlags;
9882
9883 data.llGuestProperties.push_back(prop);
9884 }
9885
9886 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9887 /* I presume this doesn't require a backup(). */
9888 mData->mGuestPropertiesModified = FALSE;
9889#endif /* VBOX_WITH_GUEST_PROPS defined */
9890
9891 *pDbg = mHWData->mDebugging;
9892 *pAutostart = mHWData->mAutostart;
9893 }
9894 catch(std::bad_alloc &)
9895 {
9896 return E_OUTOFMEMORY;
9897 }
9898
9899 AssertComRC(rc);
9900 return rc;
9901}
9902
9903/**
9904 * Saves the storage controller configuration.
9905 *
9906 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9907 */
9908HRESULT Machine::saveStorageControllers(settings::Storage &data)
9909{
9910 data.llStorageControllers.clear();
9911
9912 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9913 it != mStorageControllers->end();
9914 ++it)
9915 {
9916 HRESULT rc;
9917 ComObjPtr<StorageController> pCtl = *it;
9918
9919 settings::StorageController ctl;
9920 ctl.strName = pCtl->getName();
9921 ctl.controllerType = pCtl->getControllerType();
9922 ctl.storageBus = pCtl->getStorageBus();
9923 ctl.ulInstance = pCtl->getInstance();
9924 ctl.fBootable = pCtl->getBootable();
9925
9926 /* Save the port count. */
9927 ULONG portCount;
9928 rc = pCtl->COMGETTER(PortCount)(&portCount);
9929 ComAssertComRCRet(rc, rc);
9930 ctl.ulPortCount = portCount;
9931
9932 /* Save fUseHostIOCache */
9933 BOOL fUseHostIOCache;
9934 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9935 ComAssertComRCRet(rc, rc);
9936 ctl.fUseHostIOCache = !!fUseHostIOCache;
9937
9938 /* Save IDE emulation settings. */
9939 if (ctl.controllerType == StorageControllerType_IntelAhci)
9940 {
9941 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9942 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9943 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9944 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9945 )
9946 ComAssertComRCRet(rc, rc);
9947 }
9948
9949 /* save the devices now. */
9950 rc = saveStorageDevices(pCtl, ctl);
9951 ComAssertComRCRet(rc, rc);
9952
9953 data.llStorageControllers.push_back(ctl);
9954 }
9955
9956 return S_OK;
9957}
9958
9959/**
9960 * Saves the hard disk configuration.
9961 */
9962HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9963 settings::StorageController &data)
9964{
9965 MediaData::AttachmentList atts;
9966
9967 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9968 if (FAILED(rc)) return rc;
9969
9970 data.llAttachedDevices.clear();
9971 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9972 it != atts.end();
9973 ++it)
9974 {
9975 settings::AttachedDevice dev;
9976
9977 MediumAttachment *pAttach = *it;
9978 Medium *pMedium = pAttach->getMedium();
9979
9980 dev.deviceType = pAttach->getType();
9981 dev.lPort = pAttach->getPort();
9982 dev.lDevice = pAttach->getDevice();
9983 if (pMedium)
9984 {
9985 if (pMedium->isHostDrive())
9986 dev.strHostDriveSrc = pMedium->getLocationFull();
9987 else
9988 dev.uuid = pMedium->getId();
9989 dev.fPassThrough = pAttach->getPassthrough();
9990 dev.fTempEject = pAttach->getTempEject();
9991 dev.fNonRotational = pAttach->getNonRotational();
9992 dev.fDiscard = pAttach->getDiscard();
9993 }
9994
9995 dev.strBwGroup = pAttach->getBandwidthGroup();
9996
9997 data.llAttachedDevices.push_back(dev);
9998 }
9999
10000 return S_OK;
10001}
10002
10003/**
10004 * Saves machine state settings as defined by aFlags
10005 * (SaveSTS_* values).
10006 *
10007 * @param aFlags Combination of SaveSTS_* flags.
10008 *
10009 * @note Locks objects for writing.
10010 */
10011HRESULT Machine::saveStateSettings(int aFlags)
10012{
10013 if (aFlags == 0)
10014 return S_OK;
10015
10016 AutoCaller autoCaller(this);
10017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10018
10019 /* This object's write lock is also necessary to serialize file access
10020 * (prevent concurrent reads and writes) */
10021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10022
10023 HRESULT rc = S_OK;
10024
10025 Assert(mData->pMachineConfigFile);
10026
10027 try
10028 {
10029 if (aFlags & SaveSTS_CurStateModified)
10030 mData->pMachineConfigFile->fCurrentStateModified = true;
10031
10032 if (aFlags & SaveSTS_StateFilePath)
10033 {
10034 if (!mSSData->strStateFilePath.isEmpty())
10035 /* try to make the file name relative to the settings file dir */
10036 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10037 else
10038 mData->pMachineConfigFile->strStateFile.setNull();
10039 }
10040
10041 if (aFlags & SaveSTS_StateTimeStamp)
10042 {
10043 Assert( mData->mMachineState != MachineState_Aborted
10044 || mSSData->strStateFilePath.isEmpty());
10045
10046 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10047
10048 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10049//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10050 }
10051
10052 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10053 }
10054 catch (...)
10055 {
10056 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10057 }
10058
10059 return rc;
10060}
10061
10062/**
10063 * Ensures that the given medium is added to a media registry. If this machine
10064 * was created with 4.0 or later, then the machine registry is used. Otherwise
10065 * the global VirtualBox media registry is used.
10066 *
10067 * Caller must NOT hold machine lock, media tree or any medium locks!
10068 *
10069 * @param pMedium
10070 */
10071void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10072{
10073 /* Paranoia checks: do not hold machine or media tree locks. */
10074 AssertReturnVoid(!isWriteLockOnCurrentThread());
10075 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10076
10077 ComObjPtr<Medium> pBase;
10078 {
10079 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10080 pBase = pMedium->getBase();
10081 }
10082
10083 /* Paranoia checks: do not hold medium locks. */
10084 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10085 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10086
10087 // decide which medium registry to use now that the medium is attached:
10088 Guid uuid;
10089 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10090 // machine XML is VirtualBox 4.0 or higher:
10091 uuid = getId(); // machine UUID
10092 else
10093 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10094
10095 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10096 mParent->markRegistryModified(uuid);
10097
10098 /* For more complex hard disk structures it can happen that the base
10099 * medium isn't yet associated with any medium registry. Do that now. */
10100 if (pMedium != pBase)
10101 {
10102 if (pBase->addRegistry(uuid, true /* fRecurse */))
10103 mParent->markRegistryModified(uuid);
10104 }
10105}
10106
10107/**
10108 * Creates differencing hard disks for all normal hard disks attached to this
10109 * machine and a new set of attachments to refer to created disks.
10110 *
10111 * Used when taking a snapshot or when deleting the current state. Gets called
10112 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10113 *
10114 * This method assumes that mMediaData contains the original hard disk attachments
10115 * it needs to create diffs for. On success, these attachments will be replaced
10116 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10117 * called to delete created diffs which will also rollback mMediaData and restore
10118 * whatever was backed up before calling this method.
10119 *
10120 * Attachments with non-normal hard disks are left as is.
10121 *
10122 * If @a aOnline is @c false then the original hard disks that require implicit
10123 * diffs will be locked for reading. Otherwise it is assumed that they are
10124 * already locked for writing (when the VM was started). Note that in the latter
10125 * case it is responsibility of the caller to lock the newly created diffs for
10126 * writing if this method succeeds.
10127 *
10128 * @param aProgress Progress object to run (must contain at least as
10129 * many operations left as the number of hard disks
10130 * attached).
10131 * @param aOnline Whether the VM was online prior to this operation.
10132 *
10133 * @note The progress object is not marked as completed, neither on success nor
10134 * on failure. This is a responsibility of the caller.
10135 *
10136 * @note Locks this object and the media tree for writing.
10137 */
10138HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10139 ULONG aWeight,
10140 bool aOnline)
10141{
10142 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10143
10144 AutoCaller autoCaller(this);
10145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10146
10147 AutoMultiWriteLock2 alock(this->lockHandle(),
10148 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10149
10150 /* must be in a protective state because we release the lock below */
10151 AssertReturn( mData->mMachineState == MachineState_Saving
10152 || mData->mMachineState == MachineState_LiveSnapshotting
10153 || mData->mMachineState == MachineState_RestoringSnapshot
10154 || mData->mMachineState == MachineState_DeletingSnapshot
10155 , E_FAIL);
10156
10157 HRESULT rc = S_OK;
10158
10159 // use appropriate locked media map (online or offline)
10160 MediumLockListMap lockedMediaOffline;
10161 MediumLockListMap *lockedMediaMap;
10162 if (aOnline)
10163 lockedMediaMap = &mData->mSession.mLockedMedia;
10164 else
10165 lockedMediaMap = &lockedMediaOffline;
10166
10167 try
10168 {
10169 if (!aOnline)
10170 {
10171 /* lock all attached hard disks early to detect "in use"
10172 * situations before creating actual diffs */
10173 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10174 it != mMediaData->mAttachments.end();
10175 ++it)
10176 {
10177 MediumAttachment* pAtt = *it;
10178 if (pAtt->getType() == DeviceType_HardDisk)
10179 {
10180 Medium* pMedium = pAtt->getMedium();
10181 Assert(pMedium);
10182
10183 MediumLockList *pMediumLockList(new MediumLockList());
10184 alock.release();
10185 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10186 false /* fMediumLockWrite */,
10187 NULL,
10188 *pMediumLockList);
10189 alock.acquire();
10190 if (FAILED(rc))
10191 {
10192 delete pMediumLockList;
10193 throw rc;
10194 }
10195 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10196 if (FAILED(rc))
10197 {
10198 throw setError(rc,
10199 tr("Collecting locking information for all attached media failed"));
10200 }
10201 }
10202 }
10203
10204 /* Now lock all media. If this fails, nothing is locked. */
10205 alock.release();
10206 rc = lockedMediaMap->Lock();
10207 alock.acquire();
10208 if (FAILED(rc))
10209 {
10210 throw setError(rc,
10211 tr("Locking of attached media failed"));
10212 }
10213 }
10214
10215 /* remember the current list (note that we don't use backup() since
10216 * mMediaData may be already backed up) */
10217 MediaData::AttachmentList atts = mMediaData->mAttachments;
10218
10219 /* start from scratch */
10220 mMediaData->mAttachments.clear();
10221
10222 /* go through remembered attachments and create diffs for normal hard
10223 * disks and attach them */
10224 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10225 it != atts.end();
10226 ++it)
10227 {
10228 MediumAttachment* pAtt = *it;
10229
10230 DeviceType_T devType = pAtt->getType();
10231 Medium* pMedium = pAtt->getMedium();
10232
10233 if ( devType != DeviceType_HardDisk
10234 || pMedium == NULL
10235 || pMedium->getType() != MediumType_Normal)
10236 {
10237 /* copy the attachment as is */
10238
10239 /** @todo the progress object created in Console::TakeSnaphot
10240 * only expects operations for hard disks. Later other
10241 * device types need to show up in the progress as well. */
10242 if (devType == DeviceType_HardDisk)
10243 {
10244 if (pMedium == NULL)
10245 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10246 aWeight); // weight
10247 else
10248 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10249 pMedium->getBase()->getName().c_str()).raw(),
10250 aWeight); // weight
10251 }
10252
10253 mMediaData->mAttachments.push_back(pAtt);
10254 continue;
10255 }
10256
10257 /* need a diff */
10258 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10259 pMedium->getBase()->getName().c_str()).raw(),
10260 aWeight); // weight
10261
10262 Utf8Str strFullSnapshotFolder;
10263 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10264
10265 ComObjPtr<Medium> diff;
10266 diff.createObject();
10267 // store the diff in the same registry as the parent
10268 // (this cannot fail here because we can't create implicit diffs for
10269 // unregistered images)
10270 Guid uuidRegistryParent;
10271 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10272 Assert(fInRegistry); NOREF(fInRegistry);
10273 rc = diff->init(mParent,
10274 pMedium->getPreferredDiffFormat(),
10275 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10276 uuidRegistryParent);
10277 if (FAILED(rc)) throw rc;
10278
10279 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10280 * the push_back? Looks like we're going to release medium with the
10281 * wrong kind of lock (general issue with if we fail anywhere at all)
10282 * and an orphaned VDI in the snapshots folder. */
10283
10284 /* update the appropriate lock list */
10285 MediumLockList *pMediumLockList;
10286 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10287 AssertComRCThrowRC(rc);
10288 if (aOnline)
10289 {
10290 alock.release();
10291 /* The currently attached medium will be read-only, change
10292 * the lock type to read. */
10293 rc = pMediumLockList->Update(pMedium, false);
10294 alock.acquire();
10295 AssertComRCThrowRC(rc);
10296 }
10297
10298 /* release the locks before the potentially lengthy operation */
10299 alock.release();
10300 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10301 pMediumLockList,
10302 NULL /* aProgress */,
10303 true /* aWait */);
10304 alock.acquire();
10305 if (FAILED(rc)) throw rc;
10306
10307 rc = lockedMediaMap->Unlock();
10308 AssertComRCThrowRC(rc);
10309 alock.release();
10310 rc = pMediumLockList->Append(diff, true);
10311 alock.acquire();
10312 AssertComRCThrowRC(rc);
10313 alock.release();
10314 rc = lockedMediaMap->Lock();
10315 alock.acquire();
10316 AssertComRCThrowRC(rc);
10317
10318 rc = diff->addBackReference(mData->mUuid);
10319 AssertComRCThrowRC(rc);
10320
10321 /* add a new attachment */
10322 ComObjPtr<MediumAttachment> attachment;
10323 attachment.createObject();
10324 rc = attachment->init(this,
10325 diff,
10326 pAtt->getControllerName(),
10327 pAtt->getPort(),
10328 pAtt->getDevice(),
10329 DeviceType_HardDisk,
10330 true /* aImplicit */,
10331 false /* aPassthrough */,
10332 false /* aTempEject */,
10333 pAtt->getNonRotational(),
10334 pAtt->getDiscard(),
10335 pAtt->getBandwidthGroup());
10336 if (FAILED(rc)) throw rc;
10337
10338 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10339 AssertComRCThrowRC(rc);
10340 mMediaData->mAttachments.push_back(attachment);
10341 }
10342 }
10343 catch (HRESULT aRC) { rc = aRC; }
10344
10345 /* unlock all hard disks we locked when there is no VM */
10346 if (!aOnline)
10347 {
10348 ErrorInfoKeeper eik;
10349
10350 HRESULT rc1 = lockedMediaMap->Clear();
10351 AssertComRC(rc1);
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 and the media tree for writing.
10365 */
10366HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10367{
10368 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10369
10370 AutoCaller autoCaller(this);
10371 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10372
10373 AutoMultiWriteLock2 alock(this->lockHandle(),
10374 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10375
10376 /* We absolutely must have backed up state. */
10377 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10378
10379 HRESULT rc = S_OK;
10380 MachineState_T oldState = mData->mMachineState;
10381
10382 /* will release the lock before the potentially lengthy operation,
10383 * so protect with the special state (unless already protected) */
10384 if ( oldState != MachineState_Saving
10385 && oldState != MachineState_LiveSnapshotting
10386 && oldState != MachineState_RestoringSnapshot
10387 && oldState != MachineState_DeletingSnapshot
10388 && oldState != MachineState_DeletingSnapshotOnline
10389 && oldState != MachineState_DeletingSnapshotPaused
10390 )
10391 setMachineState(MachineState_SettingUp);
10392
10393 // use appropriate locked media map (online or offline)
10394 MediumLockListMap lockedMediaOffline;
10395 MediumLockListMap *lockedMediaMap;
10396 if (aOnline)
10397 lockedMediaMap = &mData->mSession.mLockedMedia;
10398 else
10399 lockedMediaMap = &lockedMediaOffline;
10400
10401 try
10402 {
10403 if (!aOnline)
10404 {
10405 /* lock all attached hard disks early to detect "in use"
10406 * situations before deleting actual diffs */
10407 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10408 it != mMediaData->mAttachments.end();
10409 ++it)
10410 {
10411 MediumAttachment* pAtt = *it;
10412 if (pAtt->getType() == DeviceType_HardDisk)
10413 {
10414 Medium* pMedium = pAtt->getMedium();
10415 Assert(pMedium);
10416
10417 MediumLockList *pMediumLockList(new MediumLockList());
10418 alock.release();
10419 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10420 false /* fMediumLockWrite */,
10421 NULL,
10422 *pMediumLockList);
10423 alock.acquire();
10424
10425 if (FAILED(rc))
10426 {
10427 delete pMediumLockList;
10428 throw rc;
10429 }
10430
10431 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10432 if (FAILED(rc))
10433 throw rc;
10434 }
10435 }
10436
10437 if (FAILED(rc))
10438 throw rc;
10439 } // end of offline
10440
10441 /* Lock lists are now up to date and include implicitly created media */
10442
10443 /* Go through remembered attachments and delete all implicitly created
10444 * diffs and fix up the attachment information */
10445 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10446 MediaData::AttachmentList implicitAtts;
10447 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10448 it != mMediaData->mAttachments.end();
10449 ++it)
10450 {
10451 ComObjPtr<MediumAttachment> pAtt = *it;
10452 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10453 if (pMedium.isNull())
10454 continue;
10455
10456 // Implicit attachments go on the list for deletion and back references are removed.
10457 if (pAtt->isImplicit())
10458 {
10459 /* Deassociate and mark for deletion */
10460 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10461 rc = pMedium->removeBackReference(mData->mUuid);
10462 if (FAILED(rc))
10463 throw rc;
10464 implicitAtts.push_back(pAtt);
10465 continue;
10466 }
10467
10468 /* Was this medium attached before? */
10469 if (!findAttachment(oldAtts, pMedium))
10470 {
10471 /* no: de-associate */
10472 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10473 rc = pMedium->removeBackReference(mData->mUuid);
10474 if (FAILED(rc))
10475 throw rc;
10476 continue;
10477 }
10478 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10479 }
10480
10481 /* If there are implicit attachments to delete, throw away the lock
10482 * map contents (which will unlock all media) since the medium
10483 * attachments will be rolled back. Below we need to completely
10484 * recreate the lock map anyway since it is infinitely complex to
10485 * do this incrementally (would need reconstructing each attachment
10486 * change, which would be extremely hairy). */
10487 if (implicitAtts.size() != 0)
10488 {
10489 ErrorInfoKeeper eik;
10490
10491 HRESULT rc1 = lockedMediaMap->Clear();
10492 AssertComRC(rc1);
10493 }
10494
10495 /* rollback hard disk changes */
10496 mMediaData.rollback();
10497
10498 MultiResult mrc(S_OK);
10499
10500 // Delete unused implicit diffs.
10501 if (implicitAtts.size() != 0)
10502 {
10503 alock.release();
10504
10505 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10506 it != implicitAtts.end();
10507 ++it)
10508 {
10509 // Remove medium associated with this attachment.
10510 ComObjPtr<MediumAttachment> pAtt = *it;
10511 Assert(pAtt);
10512 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10513 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10514 Assert(pMedium);
10515
10516 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10517 // continue on delete failure, just collect error messages
10518 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10519 mrc = rc;
10520 }
10521
10522 alock.acquire();
10523
10524 /* if there is a VM recreate media lock map as mentioned above,
10525 * otherwise it is a waste of time and we leave things unlocked */
10526 if (aOnline)
10527 {
10528 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10529 /* must never be NULL, but better safe than sorry */
10530 if (!pMachine.isNull())
10531 {
10532 alock.release();
10533 rc = mData->mSession.mMachine->lockMedia();
10534 alock.acquire();
10535 if (FAILED(rc))
10536 throw rc;
10537 }
10538 }
10539 }
10540 }
10541 catch (HRESULT aRC) {rc = aRC;}
10542
10543 if (mData->mMachineState == MachineState_SettingUp)
10544 setMachineState(oldState);
10545
10546 /* unlock all hard disks we locked when there is no VM */
10547 if (!aOnline)
10548 {
10549 ErrorInfoKeeper eik;
10550
10551 HRESULT rc1 = lockedMediaMap->Clear();
10552 AssertComRC(rc1);
10553 }
10554
10555 return rc;
10556}
10557
10558
10559/**
10560 * Looks through the given list of media attachments for one with the given parameters
10561 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10562 * can be searched as well if needed.
10563 *
10564 * @param list
10565 * @param aControllerName
10566 * @param aControllerPort
10567 * @param aDevice
10568 * @return
10569 */
10570MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10571 IN_BSTR aControllerName,
10572 LONG aControllerPort,
10573 LONG aDevice)
10574{
10575 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10576 it != ll.end();
10577 ++it)
10578 {
10579 MediumAttachment *pAttach = *it;
10580 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10581 return pAttach;
10582 }
10583
10584 return NULL;
10585}
10586
10587/**
10588 * Looks through the given list of media attachments for one with the given parameters
10589 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10590 * can be searched as well if needed.
10591 *
10592 * @param list
10593 * @param aControllerName
10594 * @param aControllerPort
10595 * @param aDevice
10596 * @return
10597 */
10598MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10599 ComObjPtr<Medium> pMedium)
10600{
10601 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10602 it != ll.end();
10603 ++it)
10604 {
10605 MediumAttachment *pAttach = *it;
10606 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10607 if (pMediumThis == pMedium)
10608 return pAttach;
10609 }
10610
10611 return NULL;
10612}
10613
10614/**
10615 * Looks through the given list of media attachments for one with the given parameters
10616 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10617 * can be searched as well if needed.
10618 *
10619 * @param list
10620 * @param aControllerName
10621 * @param aControllerPort
10622 * @param aDevice
10623 * @return
10624 */
10625MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10626 Guid &id)
10627{
10628 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10629 it != ll.end();
10630 ++it)
10631 {
10632 MediumAttachment *pAttach = *it;
10633 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10634 if (pMediumThis->getId() == id)
10635 return pAttach;
10636 }
10637
10638 return NULL;
10639}
10640
10641/**
10642 * Main implementation for Machine::DetachDevice. This also gets called
10643 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10644 *
10645 * @param pAttach Medium attachment to detach.
10646 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10647 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10648 * @return
10649 */
10650HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10651 AutoWriteLock &writeLock,
10652 Snapshot *pSnapshot)
10653{
10654 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10655 DeviceType_T mediumType = pAttach->getType();
10656
10657 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10658
10659 if (pAttach->isImplicit())
10660 {
10661 /* attempt to implicitly delete the implicitly created diff */
10662
10663 /// @todo move the implicit flag from MediumAttachment to Medium
10664 /// and forbid any hard disk operation when it is implicit. Or maybe
10665 /// a special media state for it to make it even more simple.
10666
10667 Assert(mMediaData.isBackedUp());
10668
10669 /* will release the lock before the potentially lengthy operation, so
10670 * protect with the special state */
10671 MachineState_T oldState = mData->mMachineState;
10672 setMachineState(MachineState_SettingUp);
10673
10674 writeLock.release();
10675
10676 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10677 true /*aWait*/);
10678
10679 writeLock.acquire();
10680
10681 setMachineState(oldState);
10682
10683 if (FAILED(rc)) return rc;
10684 }
10685
10686 setModified(IsModified_Storage);
10687 mMediaData.backup();
10688 mMediaData->mAttachments.remove(pAttach);
10689
10690 if (!oldmedium.isNull())
10691 {
10692 // if this is from a snapshot, do not defer detachment to commitMedia()
10693 if (pSnapshot)
10694 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10695 // else if non-hard disk media, do not defer detachment to commitMedia() either
10696 else if (mediumType != DeviceType_HardDisk)
10697 oldmedium->removeBackReference(mData->mUuid);
10698 }
10699
10700 return S_OK;
10701}
10702
10703/**
10704 * Goes thru all media of the given list and
10705 *
10706 * 1) calls detachDevice() on each of them for this machine and
10707 * 2) adds all Medium objects found in the process to the given list,
10708 * depending on cleanupMode.
10709 *
10710 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10711 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10712 * media to the list.
10713 *
10714 * This gets called from Machine::Unregister, both for the actual Machine and
10715 * the SnapshotMachine objects that might be found in the snapshots.
10716 *
10717 * Requires caller and locking. The machine lock must be passed in because it
10718 * will be passed on to detachDevice which needs it for temporary unlocking.
10719 *
10720 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10721 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10722 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10723 * otherwise no media get added.
10724 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10725 * @return
10726 */
10727HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10728 Snapshot *pSnapshot,
10729 CleanupMode_T cleanupMode,
10730 MediaList &llMedia)
10731{
10732 Assert(isWriteLockOnCurrentThread());
10733
10734 HRESULT rc;
10735
10736 // make a temporary list because detachDevice invalidates iterators into
10737 // mMediaData->mAttachments
10738 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10739
10740 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10741 it != llAttachments2.end();
10742 ++it)
10743 {
10744 ComObjPtr<MediumAttachment> &pAttach = *it;
10745 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10746
10747 if (!pMedium.isNull())
10748 {
10749 AutoCaller mac(pMedium);
10750 if (FAILED(mac.rc())) return mac.rc();
10751 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10752 DeviceType_T devType = pMedium->getDeviceType();
10753 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10754 && devType == DeviceType_HardDisk)
10755 || (cleanupMode == CleanupMode_Full)
10756 )
10757 {
10758 llMedia.push_back(pMedium);
10759 ComObjPtr<Medium> pParent = pMedium->getParent();
10760 /*
10761 * Search for medias which are not attached to any machine, but
10762 * in the chain to an attached disk. Mediums are only consided
10763 * if they are:
10764 * - have only one child
10765 * - no references to any machines
10766 * - are of normal medium type
10767 */
10768 while (!pParent.isNull())
10769 {
10770 AutoCaller mac1(pParent);
10771 if (FAILED(mac1.rc())) return mac1.rc();
10772 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10773 if (pParent->getChildren().size() == 1)
10774 {
10775 if ( pParent->getMachineBackRefCount() == 0
10776 && pParent->getType() == MediumType_Normal
10777 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10778 llMedia.push_back(pParent);
10779 }
10780 else
10781 break;
10782 pParent = pParent->getParent();
10783 }
10784 }
10785 }
10786
10787 // real machine: then we need to use the proper method
10788 rc = detachDevice(pAttach, writeLock, pSnapshot);
10789
10790 if (FAILED(rc))
10791 return rc;
10792 }
10793
10794 return S_OK;
10795}
10796
10797/**
10798 * Perform deferred hard disk detachments.
10799 *
10800 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10801 * backed up).
10802 *
10803 * If @a aOnline is @c true then this method will also unlock the old hard disks
10804 * for which the new implicit diffs were created and will lock these new diffs for
10805 * writing.
10806 *
10807 * @param aOnline Whether the VM was online prior to this operation.
10808 *
10809 * @note Locks this object for writing!
10810 */
10811void Machine::commitMedia(bool aOnline /*= false*/)
10812{
10813 AutoCaller autoCaller(this);
10814 AssertComRCReturnVoid(autoCaller.rc());
10815
10816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10817
10818 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10819
10820 HRESULT rc = S_OK;
10821
10822 /* no attach/detach operations -- nothing to do */
10823 if (!mMediaData.isBackedUp())
10824 return;
10825
10826 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10827 bool fMediaNeedsLocking = false;
10828
10829 /* enumerate new attachments */
10830 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10831 it != mMediaData->mAttachments.end();
10832 ++it)
10833 {
10834 MediumAttachment *pAttach = *it;
10835
10836 pAttach->commit();
10837
10838 Medium* pMedium = pAttach->getMedium();
10839 bool fImplicit = pAttach->isImplicit();
10840
10841 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10842 (pMedium) ? pMedium->getName().c_str() : "NULL",
10843 fImplicit));
10844
10845 /** @todo convert all this Machine-based voodoo to MediumAttachment
10846 * based commit logic. */
10847 if (fImplicit)
10848 {
10849 /* convert implicit attachment to normal */
10850 pAttach->setImplicit(false);
10851
10852 if ( aOnline
10853 && pMedium
10854 && pAttach->getType() == DeviceType_HardDisk
10855 )
10856 {
10857 ComObjPtr<Medium> parent = pMedium->getParent();
10858 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10859
10860 /* update the appropriate lock list */
10861 MediumLockList *pMediumLockList;
10862 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10863 AssertComRC(rc);
10864 if (pMediumLockList)
10865 {
10866 /* unlock if there's a need to change the locking */
10867 if (!fMediaNeedsLocking)
10868 {
10869 rc = mData->mSession.mLockedMedia.Unlock();
10870 AssertComRC(rc);
10871 fMediaNeedsLocking = true;
10872 }
10873 rc = pMediumLockList->Update(parent, false);
10874 AssertComRC(rc);
10875 rc = pMediumLockList->Append(pMedium, true);
10876 AssertComRC(rc);
10877 }
10878 }
10879
10880 continue;
10881 }
10882
10883 if (pMedium)
10884 {
10885 /* was this medium attached before? */
10886 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10887 oldIt != oldAtts.end();
10888 ++oldIt)
10889 {
10890 MediumAttachment *pOldAttach = *oldIt;
10891 if (pOldAttach->getMedium() == pMedium)
10892 {
10893 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10894
10895 /* yes: remove from old to avoid de-association */
10896 oldAtts.erase(oldIt);
10897 break;
10898 }
10899 }
10900 }
10901 }
10902
10903 /* enumerate remaining old attachments and de-associate from the
10904 * current machine state */
10905 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10906 it != oldAtts.end();
10907 ++it)
10908 {
10909 MediumAttachment *pAttach = *it;
10910 Medium* pMedium = pAttach->getMedium();
10911
10912 /* Detach only hard disks, since DVD/floppy media is detached
10913 * instantly in MountMedium. */
10914 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10915 {
10916 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10917
10918 /* now de-associate from the current machine state */
10919 rc = pMedium->removeBackReference(mData->mUuid);
10920 AssertComRC(rc);
10921
10922 if (aOnline)
10923 {
10924 /* unlock since medium is not used anymore */
10925 MediumLockList *pMediumLockList;
10926 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10927 AssertComRC(rc);
10928 if (pMediumLockList)
10929 {
10930 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10931 AssertComRC(rc);
10932 }
10933 }
10934 }
10935 }
10936
10937 /* take media locks again so that the locking state is consistent */
10938 if (fMediaNeedsLocking)
10939 {
10940 Assert(aOnline);
10941 rc = mData->mSession.mLockedMedia.Lock();
10942 AssertComRC(rc);
10943 }
10944
10945 /* commit the hard disk changes */
10946 mMediaData.commit();
10947
10948 if (isSessionMachine())
10949 {
10950 /*
10951 * Update the parent machine to point to the new owner.
10952 * This is necessary because the stored parent will point to the
10953 * session machine otherwise and cause crashes or errors later
10954 * when the session machine gets invalid.
10955 */
10956 /** @todo Change the MediumAttachment class to behave like any other
10957 * class in this regard by creating peer MediumAttachment
10958 * objects for session machines and share the data with the peer
10959 * machine.
10960 */
10961 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10962 it != mMediaData->mAttachments.end();
10963 ++it)
10964 {
10965 (*it)->updateParentMachine(mPeer);
10966 }
10967
10968 /* attach new data to the primary machine and reshare it */
10969 mPeer->mMediaData.attach(mMediaData);
10970 }
10971
10972 return;
10973}
10974
10975/**
10976 * Perform deferred deletion of implicitly created diffs.
10977 *
10978 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10979 * backed up).
10980 *
10981 * @note Locks this object for writing!
10982 */
10983void Machine::rollbackMedia()
10984{
10985 AutoCaller autoCaller(this);
10986 AssertComRCReturnVoid(autoCaller.rc());
10987
10988 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10989 LogFlowThisFunc(("Entering rollbackMedia\n"));
10990
10991 HRESULT rc = S_OK;
10992
10993 /* no attach/detach operations -- nothing to do */
10994 if (!mMediaData.isBackedUp())
10995 return;
10996
10997 /* enumerate new attachments */
10998 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10999 it != mMediaData->mAttachments.end();
11000 ++it)
11001 {
11002 MediumAttachment *pAttach = *it;
11003 /* Fix up the backrefs for DVD/floppy media. */
11004 if (pAttach->getType() != DeviceType_HardDisk)
11005 {
11006 Medium* pMedium = pAttach->getMedium();
11007 if (pMedium)
11008 {
11009 rc = pMedium->removeBackReference(mData->mUuid);
11010 AssertComRC(rc);
11011 }
11012 }
11013
11014 (*it)->rollback();
11015
11016 pAttach = *it;
11017 /* Fix up the backrefs for DVD/floppy media. */
11018 if (pAttach->getType() != DeviceType_HardDisk)
11019 {
11020 Medium* pMedium = pAttach->getMedium();
11021 if (pMedium)
11022 {
11023 rc = pMedium->addBackReference(mData->mUuid);
11024 AssertComRC(rc);
11025 }
11026 }
11027 }
11028
11029 /** @todo convert all this Machine-based voodoo to MediumAttachment
11030 * based rollback logic. */
11031 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11032
11033 return;
11034}
11035
11036/**
11037 * Returns true if the settings file is located in the directory named exactly
11038 * as the machine; this means, among other things, that the machine directory
11039 * should be auto-renamed.
11040 *
11041 * @param aSettingsDir if not NULL, the full machine settings file directory
11042 * name will be assigned there.
11043 *
11044 * @note Doesn't lock anything.
11045 * @note Not thread safe (must be called from this object's lock).
11046 */
11047bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11048{
11049 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11050 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11051 if (aSettingsDir)
11052 *aSettingsDir = strMachineDirName;
11053 strMachineDirName.stripPath(); // vmname
11054 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11055 strConfigFileOnly.stripPath() // vmname.vbox
11056 .stripExt(); // vmname
11057 /** @todo hack, make somehow use of ComposeMachineFilename */
11058 if (mUserData->s.fDirectoryIncludesUUID)
11059 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11060
11061 AssertReturn(!strMachineDirName.isEmpty(), false);
11062 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11063
11064 return strMachineDirName == strConfigFileOnly;
11065}
11066
11067/**
11068 * Discards all changes to machine settings.
11069 *
11070 * @param aNotify Whether to notify the direct session about changes or not.
11071 *
11072 * @note Locks objects for writing!
11073 */
11074void Machine::rollback(bool aNotify)
11075{
11076 AutoCaller autoCaller(this);
11077 AssertComRCReturn(autoCaller.rc(), (void)0);
11078
11079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11080
11081 if (!mStorageControllers.isNull())
11082 {
11083 if (mStorageControllers.isBackedUp())
11084 {
11085 /* unitialize all new devices (absent in the backed up list). */
11086 StorageControllerList::const_iterator it = mStorageControllers->begin();
11087 StorageControllerList *backedList = mStorageControllers.backedUpData();
11088 while (it != mStorageControllers->end())
11089 {
11090 if ( std::find(backedList->begin(), backedList->end(), *it)
11091 == backedList->end()
11092 )
11093 {
11094 (*it)->uninit();
11095 }
11096 ++it;
11097 }
11098
11099 /* restore the list */
11100 mStorageControllers.rollback();
11101 }
11102
11103 /* rollback any changes to devices after restoring the list */
11104 if (mData->flModifications & IsModified_Storage)
11105 {
11106 StorageControllerList::const_iterator it = mStorageControllers->begin();
11107 while (it != mStorageControllers->end())
11108 {
11109 (*it)->rollback();
11110 ++it;
11111 }
11112 }
11113 }
11114
11115 mUserData.rollback();
11116
11117 mHWData.rollback();
11118
11119 if (mData->flModifications & IsModified_Storage)
11120 rollbackMedia();
11121
11122 if (mBIOSSettings)
11123 mBIOSSettings->rollback();
11124
11125 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11126 mVRDEServer->rollback();
11127
11128 if (mAudioAdapter)
11129 mAudioAdapter->rollback();
11130
11131 if (mUSBController && (mData->flModifications & IsModified_USB))
11132 mUSBController->rollback();
11133
11134 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11135 mBandwidthControl->rollback();
11136
11137 if (!mHWData.isNull())
11138 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11139 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11140 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11141 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11142
11143 if (mData->flModifications & IsModified_NetworkAdapters)
11144 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11145 if ( mNetworkAdapters[slot]
11146 && mNetworkAdapters[slot]->isModified())
11147 {
11148 mNetworkAdapters[slot]->rollback();
11149 networkAdapters[slot] = mNetworkAdapters[slot];
11150 }
11151
11152 if (mData->flModifications & IsModified_SerialPorts)
11153 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11154 if ( mSerialPorts[slot]
11155 && mSerialPorts[slot]->isModified())
11156 {
11157 mSerialPorts[slot]->rollback();
11158 serialPorts[slot] = mSerialPorts[slot];
11159 }
11160
11161 if (mData->flModifications & IsModified_ParallelPorts)
11162 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11163 if ( mParallelPorts[slot]
11164 && mParallelPorts[slot]->isModified())
11165 {
11166 mParallelPorts[slot]->rollback();
11167 parallelPorts[slot] = mParallelPorts[slot];
11168 }
11169
11170 if (aNotify)
11171 {
11172 /* inform the direct session about changes */
11173
11174 ComObjPtr<Machine> that = this;
11175 uint32_t flModifications = mData->flModifications;
11176 alock.release();
11177
11178 if (flModifications & IsModified_SharedFolders)
11179 that->onSharedFolderChange();
11180
11181 if (flModifications & IsModified_VRDEServer)
11182 that->onVRDEServerChange(/* aRestart */ TRUE);
11183 if (flModifications & IsModified_USB)
11184 that->onUSBControllerChange();
11185
11186 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11187 if (networkAdapters[slot])
11188 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11189 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11190 if (serialPorts[slot])
11191 that->onSerialPortChange(serialPorts[slot]);
11192 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11193 if (parallelPorts[slot])
11194 that->onParallelPortChange(parallelPorts[slot]);
11195
11196 if (flModifications & IsModified_Storage)
11197 that->onStorageControllerChange();
11198
11199#if 0
11200 if (flModifications & IsModified_BandwidthControl)
11201 that->onBandwidthControlChange();
11202#endif
11203 }
11204}
11205
11206/**
11207 * Commits all the changes to machine settings.
11208 *
11209 * Note that this operation is supposed to never fail.
11210 *
11211 * @note Locks this object and children for writing.
11212 */
11213void Machine::commit()
11214{
11215 AutoCaller autoCaller(this);
11216 AssertComRCReturnVoid(autoCaller.rc());
11217
11218 AutoCaller peerCaller(mPeer);
11219 AssertComRCReturnVoid(peerCaller.rc());
11220
11221 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11222
11223 /*
11224 * use safe commit to ensure Snapshot machines (that share mUserData)
11225 * will still refer to a valid memory location
11226 */
11227 mUserData.commitCopy();
11228
11229 mHWData.commit();
11230
11231 if (mMediaData.isBackedUp())
11232 commitMedia();
11233
11234 mBIOSSettings->commit();
11235 mVRDEServer->commit();
11236 mAudioAdapter->commit();
11237 mUSBController->commit();
11238 mBandwidthControl->commit();
11239
11240 /* Since mNetworkAdapters is a list which might have been changed (resized)
11241 * without using the Backupable<> template we need to handle the copying
11242 * of the list entries manually, including the creation of peers for the
11243 * new objects. */
11244 bool commitNetworkAdapters = false;
11245 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11246 if (mPeer)
11247 {
11248 /* commit everything, even the ones which will go away */
11249 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11250 mNetworkAdapters[slot]->commit();
11251 /* copy over the new entries, creating a peer and uninit the original */
11252 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11253 for (size_t slot = 0; slot < newSize; slot++)
11254 {
11255 /* look if this adapter has a peer device */
11256 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11257 if (!peer)
11258 {
11259 /* no peer means the adapter is a newly created one;
11260 * create a peer owning data this data share it with */
11261 peer.createObject();
11262 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11263 }
11264 mPeer->mNetworkAdapters[slot] = peer;
11265 }
11266 /* uninit any no longer needed network adapters */
11267 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11268 mNetworkAdapters[slot]->uninit();
11269 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11270 {
11271 if (mPeer->mNetworkAdapters[slot])
11272 mPeer->mNetworkAdapters[slot]->uninit();
11273 }
11274 /* Keep the original network adapter count until this point, so that
11275 * discarding a chipset type change will not lose settings. */
11276 mNetworkAdapters.resize(newSize);
11277 mPeer->mNetworkAdapters.resize(newSize);
11278 }
11279 else
11280 {
11281 /* we have no peer (our parent is the newly created machine);
11282 * just commit changes to the network adapters */
11283 commitNetworkAdapters = true;
11284 }
11285 if (commitNetworkAdapters)
11286 {
11287 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11288 mNetworkAdapters[slot]->commit();
11289 }
11290
11291 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11292 mSerialPorts[slot]->commit();
11293 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11294 mParallelPorts[slot]->commit();
11295
11296 bool commitStorageControllers = false;
11297
11298 if (mStorageControllers.isBackedUp())
11299 {
11300 mStorageControllers.commit();
11301
11302 if (mPeer)
11303 {
11304 /* Commit all changes to new controllers (this will reshare data with
11305 * peers for those who have peers) */
11306 StorageControllerList *newList = new StorageControllerList();
11307 StorageControllerList::const_iterator it = mStorageControllers->begin();
11308 while (it != mStorageControllers->end())
11309 {
11310 (*it)->commit();
11311
11312 /* look if this controller has a peer device */
11313 ComObjPtr<StorageController> peer = (*it)->getPeer();
11314 if (!peer)
11315 {
11316 /* no peer means the device is a newly created one;
11317 * create a peer owning data this device share it with */
11318 peer.createObject();
11319 peer->init(mPeer, *it, true /* aReshare */);
11320 }
11321 else
11322 {
11323 /* remove peer from the old list */
11324 mPeer->mStorageControllers->remove(peer);
11325 }
11326 /* and add it to the new list */
11327 newList->push_back(peer);
11328
11329 ++it;
11330 }
11331
11332 /* uninit old peer's controllers that are left */
11333 it = mPeer->mStorageControllers->begin();
11334 while (it != mPeer->mStorageControllers->end())
11335 {
11336 (*it)->uninit();
11337 ++it;
11338 }
11339
11340 /* attach new list of controllers to our peer */
11341 mPeer->mStorageControllers.attach(newList);
11342 }
11343 else
11344 {
11345 /* we have no peer (our parent is the newly created machine);
11346 * just commit changes to devices */
11347 commitStorageControllers = true;
11348 }
11349 }
11350 else
11351 {
11352 /* the list of controllers itself is not changed,
11353 * just commit changes to controllers themselves */
11354 commitStorageControllers = true;
11355 }
11356
11357 if (commitStorageControllers)
11358 {
11359 StorageControllerList::const_iterator it = mStorageControllers->begin();
11360 while (it != mStorageControllers->end())
11361 {
11362 (*it)->commit();
11363 ++it;
11364 }
11365 }
11366
11367 if (isSessionMachine())
11368 {
11369 /* attach new data to the primary machine and reshare it */
11370 mPeer->mUserData.attach(mUserData);
11371 mPeer->mHWData.attach(mHWData);
11372 /* mMediaData is reshared by fixupMedia */
11373 // mPeer->mMediaData.attach(mMediaData);
11374 Assert(mPeer->mMediaData.data() == mMediaData.data());
11375 }
11376}
11377
11378/**
11379 * Copies all the hardware data from the given machine.
11380 *
11381 * Currently, only called when the VM is being restored from a snapshot. In
11382 * particular, this implies that the VM is not running during this method's
11383 * call.
11384 *
11385 * @note This method must be called from under this object's lock.
11386 *
11387 * @note This method doesn't call #commit(), so all data remains backed up and
11388 * unsaved.
11389 */
11390void Machine::copyFrom(Machine *aThat)
11391{
11392 AssertReturnVoid(!isSnapshotMachine());
11393 AssertReturnVoid(aThat->isSnapshotMachine());
11394
11395 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11396
11397 mHWData.assignCopy(aThat->mHWData);
11398
11399 // create copies of all shared folders (mHWData after attaching a copy
11400 // contains just references to original objects)
11401 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11402 it != mHWData->mSharedFolders.end();
11403 ++it)
11404 {
11405 ComObjPtr<SharedFolder> folder;
11406 folder.createObject();
11407 HRESULT rc = folder->initCopy(getMachine(), *it);
11408 AssertComRC(rc);
11409 *it = folder;
11410 }
11411
11412 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11413 mVRDEServer->copyFrom(aThat->mVRDEServer);
11414 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11415 mUSBController->copyFrom(aThat->mUSBController);
11416 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11417
11418 /* create private copies of all controllers */
11419 mStorageControllers.backup();
11420 mStorageControllers->clear();
11421 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11422 it != aThat->mStorageControllers->end();
11423 ++it)
11424 {
11425 ComObjPtr<StorageController> ctrl;
11426 ctrl.createObject();
11427 ctrl->initCopy(this, *it);
11428 mStorageControllers->push_back(ctrl);
11429 }
11430
11431 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11432 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11433 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11434 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11435 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11436 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11437 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11438}
11439
11440/**
11441 * Returns whether the given storage controller is hotplug capable.
11442 *
11443 * @returns true if the controller supports hotplugging
11444 * false otherwise.
11445 * @param enmCtrlType The controller type to check for.
11446 */
11447bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11448{
11449 switch (enmCtrlType)
11450 {
11451 case StorageControllerType_IntelAhci:
11452 return true;
11453 case StorageControllerType_LsiLogic:
11454 case StorageControllerType_LsiLogicSas:
11455 case StorageControllerType_BusLogic:
11456 case StorageControllerType_PIIX3:
11457 case StorageControllerType_PIIX4:
11458 case StorageControllerType_ICH6:
11459 case StorageControllerType_I82078:
11460 default:
11461 return false;
11462 }
11463}
11464
11465#ifdef VBOX_WITH_RESOURCE_USAGE_API
11466
11467void Machine::getDiskList(MediaList &list)
11468{
11469 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11470 it != mMediaData->mAttachments.end();
11471 ++it)
11472 {
11473 MediumAttachment* pAttach = *it;
11474 /* just in case */
11475 AssertStmt(pAttach, continue);
11476
11477 AutoCaller localAutoCallerA(pAttach);
11478 if (FAILED(localAutoCallerA.rc())) continue;
11479
11480 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11481
11482 if (pAttach->getType() == DeviceType_HardDisk)
11483 list.push_back(pAttach->getMedium());
11484 }
11485}
11486
11487void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11488{
11489 AssertReturnVoid(isWriteLockOnCurrentThread());
11490 AssertPtrReturnVoid(aCollector);
11491
11492 pm::CollectorHAL *hal = aCollector->getHAL();
11493 /* Create sub metrics */
11494 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11495 "Percentage of processor time spent in user mode by the VM process.");
11496 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11497 "Percentage of processor time spent in kernel mode by the VM process.");
11498 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11499 "Size of resident portion of VM process in memory.");
11500 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11501 "Actual size of all VM disks combined.");
11502 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11503 "Network receive rate.");
11504 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11505 "Network transmit rate.");
11506 /* Create and register base metrics */
11507 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11508 cpuLoadUser, cpuLoadKernel);
11509 aCollector->registerBaseMetric(cpuLoad);
11510 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11511 ramUsageUsed);
11512 aCollector->registerBaseMetric(ramUsage);
11513 MediaList disks;
11514 getDiskList(disks);
11515 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11516 diskUsageUsed);
11517 aCollector->registerBaseMetric(diskUsage);
11518
11519 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11520 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11521 new pm::AggregateAvg()));
11522 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11523 new pm::AggregateMin()));
11524 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11525 new pm::AggregateMax()));
11526 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11527 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11528 new pm::AggregateAvg()));
11529 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11530 new pm::AggregateMin()));
11531 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11532 new pm::AggregateMax()));
11533
11534 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11535 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11536 new pm::AggregateAvg()));
11537 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11538 new pm::AggregateMin()));
11539 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11540 new pm::AggregateMax()));
11541
11542 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11543 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11544 new pm::AggregateAvg()));
11545 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11546 new pm::AggregateMin()));
11547 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11548 new pm::AggregateMax()));
11549
11550
11551 /* Guest metrics collector */
11552 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11553 aCollector->registerGuest(mCollectorGuest);
11554 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11555 this, __PRETTY_FUNCTION__, mCollectorGuest));
11556
11557 /* Create sub metrics */
11558 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11559 "Percentage of processor time spent in user mode as seen by the guest.");
11560 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11561 "Percentage of processor time spent in kernel mode as seen by the guest.");
11562 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11563 "Percentage of processor time spent idling as seen by the guest.");
11564
11565 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11566 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11567 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11568 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11569 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11570 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11571
11572 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11573
11574 /* Create and register base metrics */
11575 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11576 machineNetRx, machineNetTx);
11577 aCollector->registerBaseMetric(machineNetRate);
11578
11579 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11580 guestLoadUser, guestLoadKernel, guestLoadIdle);
11581 aCollector->registerBaseMetric(guestCpuLoad);
11582
11583 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11584 guestMemTotal, guestMemFree,
11585 guestMemBalloon, guestMemShared,
11586 guestMemCache, guestPagedTotal);
11587 aCollector->registerBaseMetric(guestCpuMem);
11588
11589 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11590 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11591 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11592 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11593
11594 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11595 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11596 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11597 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11598
11599 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11600 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11601 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11602 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11603
11604 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11605 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11606 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11607 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11608
11609 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11610 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11611 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11612 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11613
11614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11618
11619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11623
11624 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11626 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11627 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11628
11629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11633
11634 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11635 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11636 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11637 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11638
11639 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11640 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11641 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11642 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11643}
11644
11645void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11646{
11647 AssertReturnVoid(isWriteLockOnCurrentThread());
11648
11649 if (aCollector)
11650 {
11651 aCollector->unregisterMetricsFor(aMachine);
11652 aCollector->unregisterBaseMetricsFor(aMachine);
11653 }
11654}
11655
11656#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11657
11658
11659////////////////////////////////////////////////////////////////////////////////
11660
11661DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11662
11663HRESULT SessionMachine::FinalConstruct()
11664{
11665 LogFlowThisFunc(("\n"));
11666
11667#if defined(RT_OS_WINDOWS)
11668 mIPCSem = NULL;
11669#elif defined(RT_OS_OS2)
11670 mIPCSem = NULLHANDLE;
11671#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11672 mIPCSem = -1;
11673#else
11674# error "Port me!"
11675#endif
11676
11677 return BaseFinalConstruct();
11678}
11679
11680void SessionMachine::FinalRelease()
11681{
11682 LogFlowThisFunc(("\n"));
11683
11684 uninit(Uninit::Unexpected);
11685
11686 BaseFinalRelease();
11687}
11688
11689/**
11690 * @note Must be called only by Machine::openSession() from its own write lock.
11691 */
11692HRESULT SessionMachine::init(Machine *aMachine)
11693{
11694 LogFlowThisFuncEnter();
11695 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11696
11697 AssertReturn(aMachine, E_INVALIDARG);
11698
11699 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11700
11701 /* Enclose the state transition NotReady->InInit->Ready */
11702 AutoInitSpan autoInitSpan(this);
11703 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11704
11705 /* create the interprocess semaphore */
11706#if defined(RT_OS_WINDOWS)
11707 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11708 for (size_t i = 0; i < mIPCSemName.length(); i++)
11709 if (mIPCSemName.raw()[i] == '\\')
11710 mIPCSemName.raw()[i] = '/';
11711 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11712 ComAssertMsgRet(mIPCSem,
11713 ("Cannot create IPC mutex '%ls', err=%d",
11714 mIPCSemName.raw(), ::GetLastError()),
11715 E_FAIL);
11716#elif defined(RT_OS_OS2)
11717 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11718 aMachine->mData->mUuid.raw());
11719 mIPCSemName = ipcSem;
11720 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11721 ComAssertMsgRet(arc == NO_ERROR,
11722 ("Cannot create IPC mutex '%s', arc=%ld",
11723 ipcSem.c_str(), arc),
11724 E_FAIL);
11725#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11726# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11727# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11728 /** @todo Check that this still works correctly. */
11729 AssertCompileSize(key_t, 8);
11730# else
11731 AssertCompileSize(key_t, 4);
11732# endif
11733 key_t key;
11734 mIPCSem = -1;
11735 mIPCKey = "0";
11736 for (uint32_t i = 0; i < 1 << 24; i++)
11737 {
11738 key = ((uint32_t)'V' << 24) | i;
11739 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11740 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11741 {
11742 mIPCSem = sem;
11743 if (sem >= 0)
11744 mIPCKey = BstrFmt("%u", key);
11745 break;
11746 }
11747 }
11748# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11749 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11750 char *pszSemName = NULL;
11751 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11752 key_t key = ::ftok(pszSemName, 'V');
11753 RTStrFree(pszSemName);
11754
11755 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11756# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11757
11758 int errnoSave = errno;
11759 if (mIPCSem < 0 && errnoSave == ENOSYS)
11760 {
11761 setError(E_FAIL,
11762 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11763 "support for SysV IPC. Check the host kernel configuration for "
11764 "CONFIG_SYSVIPC=y"));
11765 return E_FAIL;
11766 }
11767 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11768 * the IPC semaphores */
11769 if (mIPCSem < 0 && errnoSave == ENOSPC)
11770 {
11771#ifdef RT_OS_LINUX
11772 setError(E_FAIL,
11773 tr("Cannot create IPC semaphore because the system limit for the "
11774 "maximum number of semaphore sets (SEMMNI), or the system wide "
11775 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11776 "current set of SysV IPC semaphores can be determined from "
11777 "the file /proc/sysvipc/sem"));
11778#else
11779 setError(E_FAIL,
11780 tr("Cannot create IPC semaphore because the system-imposed limit "
11781 "on the maximum number of allowed semaphores or semaphore "
11782 "identifiers system-wide would be exceeded"));
11783#endif
11784 return E_FAIL;
11785 }
11786 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11787 E_FAIL);
11788 /* set the initial value to 1 */
11789 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11790 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11791 E_FAIL);
11792#else
11793# error "Port me!"
11794#endif
11795
11796 /* memorize the peer Machine */
11797 unconst(mPeer) = aMachine;
11798 /* share the parent pointer */
11799 unconst(mParent) = aMachine->mParent;
11800
11801 /* take the pointers to data to share */
11802 mData.share(aMachine->mData);
11803 mSSData.share(aMachine->mSSData);
11804
11805 mUserData.share(aMachine->mUserData);
11806 mHWData.share(aMachine->mHWData);
11807 mMediaData.share(aMachine->mMediaData);
11808
11809 mStorageControllers.allocate();
11810 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11811 it != aMachine->mStorageControllers->end();
11812 ++it)
11813 {
11814 ComObjPtr<StorageController> ctl;
11815 ctl.createObject();
11816 ctl->init(this, *it);
11817 mStorageControllers->push_back(ctl);
11818 }
11819
11820 unconst(mBIOSSettings).createObject();
11821 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11822 /* create another VRDEServer object that will be mutable */
11823 unconst(mVRDEServer).createObject();
11824 mVRDEServer->init(this, aMachine->mVRDEServer);
11825 /* create another audio adapter object that will be mutable */
11826 unconst(mAudioAdapter).createObject();
11827 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11828 /* create a list of serial ports that will be mutable */
11829 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11830 {
11831 unconst(mSerialPorts[slot]).createObject();
11832 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11833 }
11834 /* create a list of parallel ports that will be mutable */
11835 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11836 {
11837 unconst(mParallelPorts[slot]).createObject();
11838 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11839 }
11840 /* create another USB controller object that will be mutable */
11841 unconst(mUSBController).createObject();
11842 mUSBController->init(this, aMachine->mUSBController);
11843
11844 /* create a list of network adapters that will be mutable */
11845 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11846 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11847 {
11848 unconst(mNetworkAdapters[slot]).createObject();
11849 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11850 }
11851
11852 /* create another bandwidth control object that will be mutable */
11853 unconst(mBandwidthControl).createObject();
11854 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11855
11856 /* default is to delete saved state on Saved -> PoweredOff transition */
11857 mRemoveSavedState = true;
11858
11859 /* Confirm a successful initialization when it's the case */
11860 autoInitSpan.setSucceeded();
11861
11862 LogFlowThisFuncLeave();
11863 return S_OK;
11864}
11865
11866/**
11867 * Uninitializes this session object. If the reason is other than
11868 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11869 *
11870 * @param aReason uninitialization reason
11871 *
11872 * @note Locks mParent + this object for writing.
11873 */
11874void SessionMachine::uninit(Uninit::Reason aReason)
11875{
11876 LogFlowThisFuncEnter();
11877 LogFlowThisFunc(("reason=%d\n", aReason));
11878
11879 /*
11880 * Strongly reference ourselves to prevent this object deletion after
11881 * mData->mSession.mMachine.setNull() below (which can release the last
11882 * reference and call the destructor). Important: this must be done before
11883 * accessing any members (and before AutoUninitSpan that does it as well).
11884 * This self reference will be released as the very last step on return.
11885 */
11886 ComObjPtr<SessionMachine> selfRef = this;
11887
11888 /* Enclose the state transition Ready->InUninit->NotReady */
11889 AutoUninitSpan autoUninitSpan(this);
11890 if (autoUninitSpan.uninitDone())
11891 {
11892 LogFlowThisFunc(("Already uninitialized\n"));
11893 LogFlowThisFuncLeave();
11894 return;
11895 }
11896
11897 if (autoUninitSpan.initFailed())
11898 {
11899 /* We've been called by init() because it's failed. It's not really
11900 * necessary (nor it's safe) to perform the regular uninit sequence
11901 * below, the following is enough.
11902 */
11903 LogFlowThisFunc(("Initialization failed.\n"));
11904#if defined(RT_OS_WINDOWS)
11905 if (mIPCSem)
11906 ::CloseHandle(mIPCSem);
11907 mIPCSem = NULL;
11908#elif defined(RT_OS_OS2)
11909 if (mIPCSem != NULLHANDLE)
11910 ::DosCloseMutexSem(mIPCSem);
11911 mIPCSem = NULLHANDLE;
11912#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11913 if (mIPCSem >= 0)
11914 ::semctl(mIPCSem, 0, IPC_RMID);
11915 mIPCSem = -1;
11916# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11917 mIPCKey = "0";
11918# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11919#else
11920# error "Port me!"
11921#endif
11922 uninitDataAndChildObjects();
11923 mData.free();
11924 unconst(mParent) = NULL;
11925 unconst(mPeer) = NULL;
11926 LogFlowThisFuncLeave();
11927 return;
11928 }
11929
11930 MachineState_T lastState;
11931 {
11932 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11933 lastState = mData->mMachineState;
11934 }
11935 NOREF(lastState);
11936
11937#ifdef VBOX_WITH_USB
11938 // release all captured USB devices, but do this before requesting the locks below
11939 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11940 {
11941 /* Console::captureUSBDevices() is called in the VM process only after
11942 * setting the machine state to Starting or Restoring.
11943 * Console::detachAllUSBDevices() will be called upon successful
11944 * termination. So, we need to release USB devices only if there was
11945 * an abnormal termination of a running VM.
11946 *
11947 * This is identical to SessionMachine::DetachAllUSBDevices except
11948 * for the aAbnormal argument. */
11949 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11950 AssertComRC(rc);
11951 NOREF(rc);
11952
11953 USBProxyService *service = mParent->host()->usbProxyService();
11954 if (service)
11955 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11956 }
11957#endif /* VBOX_WITH_USB */
11958
11959 // we need to lock this object in uninit() because the lock is shared
11960 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11961 // and others need mParent lock, and USB needs host lock.
11962 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11963
11964#if 0
11965 // Trigger async cleanup tasks, avoid doing things here which are not
11966 // vital to be done immediately and maybe need more locks. This calls
11967 // Machine::unregisterMetrics().
11968 mParent->onMachineUninit(mPeer);
11969#else
11970 /*
11971 * It is safe to call Machine::unregisterMetrics() here because
11972 * PerformanceCollector::samplerCallback no longer accesses guest methods
11973 * holding the lock.
11974 */
11975 unregisterMetrics(mParent->performanceCollector(), mPeer);
11976#endif
11977 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11978 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11979 this, __PRETTY_FUNCTION__, mCollectorGuest));
11980 if (mCollectorGuest)
11981 {
11982 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11983 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11984 mCollectorGuest = NULL;
11985 }
11986
11987 if (aReason == Uninit::Abnormal)
11988 {
11989 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11990 Global::IsOnlineOrTransient(lastState)));
11991
11992 /* reset the state to Aborted */
11993 if (mData->mMachineState != MachineState_Aborted)
11994 setMachineState(MachineState_Aborted);
11995 }
11996
11997 // any machine settings modified?
11998 if (mData->flModifications)
11999 {
12000 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12001 rollback(false /* aNotify */);
12002 }
12003
12004 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12005 || !mConsoleTaskData.mSnapshot);
12006 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12007 {
12008 LogWarningThisFunc(("canceling failed save state request!\n"));
12009 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12010 }
12011 else if (!mConsoleTaskData.mSnapshot.isNull())
12012 {
12013 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12014
12015 /* delete all differencing hard disks created (this will also attach
12016 * their parents back by rolling back mMediaData) */
12017 rollbackMedia();
12018
12019 // delete the saved state file (it might have been already created)
12020 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12021 // think it's still in use
12022 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12023 mConsoleTaskData.mSnapshot->uninit();
12024 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12025 }
12026
12027 if (!mData->mSession.mType.isEmpty())
12028 {
12029 /* mType is not null when this machine's process has been started by
12030 * Machine::LaunchVMProcess(), therefore it is our child. We
12031 * need to queue the PID to reap the process (and avoid zombies on
12032 * Linux). */
12033 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12034 mParent->addProcessToReap(mData->mSession.mPID);
12035 }
12036
12037 mData->mSession.mPID = NIL_RTPROCESS;
12038
12039 if (aReason == Uninit::Unexpected)
12040 {
12041 /* Uninitialization didn't come from #checkForDeath(), so tell the
12042 * client watcher thread to update the set of machines that have open
12043 * sessions. */
12044 mParent->updateClientWatcher();
12045 }
12046
12047 /* uninitialize all remote controls */
12048 if (mData->mSession.mRemoteControls.size())
12049 {
12050 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12051 mData->mSession.mRemoteControls.size()));
12052
12053 Data::Session::RemoteControlList::iterator it =
12054 mData->mSession.mRemoteControls.begin();
12055 while (it != mData->mSession.mRemoteControls.end())
12056 {
12057 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12058 HRESULT rc = (*it)->Uninitialize();
12059 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12060 if (FAILED(rc))
12061 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12062 ++it;
12063 }
12064 mData->mSession.mRemoteControls.clear();
12065 }
12066
12067 /*
12068 * An expected uninitialization can come only from #checkForDeath().
12069 * Otherwise it means that something's gone really wrong (for example,
12070 * the Session implementation has released the VirtualBox reference
12071 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12072 * etc). However, it's also possible, that the client releases the IPC
12073 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12074 * but the VirtualBox release event comes first to the server process.
12075 * This case is practically possible, so we should not assert on an
12076 * unexpected uninit, just log a warning.
12077 */
12078
12079 if ((aReason == Uninit::Unexpected))
12080 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12081
12082 if (aReason != Uninit::Normal)
12083 {
12084 mData->mSession.mDirectControl.setNull();
12085 }
12086 else
12087 {
12088 /* this must be null here (see #OnSessionEnd()) */
12089 Assert(mData->mSession.mDirectControl.isNull());
12090 Assert(mData->mSession.mState == SessionState_Unlocking);
12091 Assert(!mData->mSession.mProgress.isNull());
12092 }
12093 if (mData->mSession.mProgress)
12094 {
12095 if (aReason == Uninit::Normal)
12096 mData->mSession.mProgress->notifyComplete(S_OK);
12097 else
12098 mData->mSession.mProgress->notifyComplete(E_FAIL,
12099 COM_IIDOF(ISession),
12100 getComponentName(),
12101 tr("The VM session was aborted"));
12102 mData->mSession.mProgress.setNull();
12103 }
12104
12105 /* remove the association between the peer machine and this session machine */
12106 Assert( (SessionMachine*)mData->mSession.mMachine == this
12107 || aReason == Uninit::Unexpected);
12108
12109 /* reset the rest of session data */
12110 mData->mSession.mMachine.setNull();
12111 mData->mSession.mState = SessionState_Unlocked;
12112 mData->mSession.mType.setNull();
12113
12114 /* close the interprocess semaphore before leaving the exclusive lock */
12115#if defined(RT_OS_WINDOWS)
12116 if (mIPCSem)
12117 ::CloseHandle(mIPCSem);
12118 mIPCSem = NULL;
12119#elif defined(RT_OS_OS2)
12120 if (mIPCSem != NULLHANDLE)
12121 ::DosCloseMutexSem(mIPCSem);
12122 mIPCSem = NULLHANDLE;
12123#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12124 if (mIPCSem >= 0)
12125 ::semctl(mIPCSem, 0, IPC_RMID);
12126 mIPCSem = -1;
12127# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12128 mIPCKey = "0";
12129# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12130#else
12131# error "Port me!"
12132#endif
12133
12134 /* fire an event */
12135 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12136
12137 uninitDataAndChildObjects();
12138
12139 /* free the essential data structure last */
12140 mData.free();
12141
12142 /* release the exclusive lock before setting the below two to NULL */
12143 multilock.release();
12144
12145 unconst(mParent) = NULL;
12146 unconst(mPeer) = NULL;
12147
12148 LogFlowThisFuncLeave();
12149}
12150
12151// util::Lockable interface
12152////////////////////////////////////////////////////////////////////////////////
12153
12154/**
12155 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12156 * with the primary Machine instance (mPeer).
12157 */
12158RWLockHandle *SessionMachine::lockHandle() const
12159{
12160 AssertReturn(mPeer != NULL, NULL);
12161 return mPeer->lockHandle();
12162}
12163
12164// IInternalMachineControl methods
12165////////////////////////////////////////////////////////////////////////////////
12166
12167/**
12168 * Passes collected guest statistics to performance collector object
12169 */
12170STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12171 ULONG aCpuKernel, ULONG aCpuIdle,
12172 ULONG aMemTotal, ULONG aMemFree,
12173 ULONG aMemBalloon, ULONG aMemShared,
12174 ULONG aMemCache, ULONG aPageTotal,
12175 ULONG aAllocVMM, ULONG aFreeVMM,
12176 ULONG aBalloonedVMM, ULONG aSharedVMM,
12177 ULONG aVmNetRx, ULONG aVmNetTx)
12178{
12179 if (mCollectorGuest)
12180 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12181 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12182 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12183 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12184
12185 return S_OK;
12186}
12187
12188/**
12189 * @note Locks this object for writing.
12190 */
12191STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12192{
12193 AutoCaller autoCaller(this);
12194 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12195
12196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12197
12198 mRemoveSavedState = aRemove;
12199
12200 return S_OK;
12201}
12202
12203/**
12204 * @note Locks the same as #setMachineState() does.
12205 */
12206STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12207{
12208 return setMachineState(aMachineState);
12209}
12210
12211/**
12212 * @note Locks this object for reading.
12213 */
12214STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12215{
12216 AutoCaller autoCaller(this);
12217 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12218
12219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12220
12221#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12222 mIPCSemName.cloneTo(aId);
12223 return S_OK;
12224#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12225# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12226 mIPCKey.cloneTo(aId);
12227# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12228 mData->m_strConfigFileFull.cloneTo(aId);
12229# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12230 return S_OK;
12231#else
12232# error "Port me!"
12233#endif
12234}
12235
12236/**
12237 * @note Locks this object for writing.
12238 */
12239STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12240{
12241 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12242 AutoCaller autoCaller(this);
12243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12244
12245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12246
12247 if (mData->mSession.mState != SessionState_Locked)
12248 return VBOX_E_INVALID_OBJECT_STATE;
12249
12250 if (!mData->mSession.mProgress.isNull())
12251 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12252
12253 LogFlowThisFunc(("returns S_OK.\n"));
12254 return S_OK;
12255}
12256
12257/**
12258 * @note Locks this object for writing.
12259 */
12260STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12261{
12262 AutoCaller autoCaller(this);
12263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12264
12265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12266
12267 if (mData->mSession.mState != SessionState_Locked)
12268 return VBOX_E_INVALID_OBJECT_STATE;
12269
12270 /* Finalize the LaunchVMProcess progress object. */
12271 if (mData->mSession.mProgress)
12272 {
12273 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12274 mData->mSession.mProgress.setNull();
12275 }
12276
12277 if (SUCCEEDED((HRESULT)iResult))
12278 {
12279#ifdef VBOX_WITH_RESOURCE_USAGE_API
12280 /* The VM has been powered up successfully, so it makes sense
12281 * now to offer the performance metrics for a running machine
12282 * object. Doing it earlier wouldn't be safe. */
12283 registerMetrics(mParent->performanceCollector(), mPeer,
12284 mData->mSession.mPID);
12285#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12286 }
12287
12288 return S_OK;
12289}
12290
12291/**
12292 * @note Locks this object for writing.
12293 */
12294STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12295{
12296 LogFlowThisFuncEnter();
12297
12298 CheckComArgOutPointerValid(aProgress);
12299
12300 AutoCaller autoCaller(this);
12301 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12302
12303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12304
12305 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12306 E_FAIL);
12307
12308 /* create a progress object to track operation completion */
12309 ComObjPtr<Progress> pProgress;
12310 pProgress.createObject();
12311 pProgress->init(getVirtualBox(),
12312 static_cast<IMachine *>(this) /* aInitiator */,
12313 Bstr(tr("Stopping the virtual machine")).raw(),
12314 FALSE /* aCancelable */);
12315
12316 /* fill in the console task data */
12317 mConsoleTaskData.mLastState = mData->mMachineState;
12318 mConsoleTaskData.mProgress = pProgress;
12319
12320 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12321 setMachineState(MachineState_Stopping);
12322
12323 pProgress.queryInterfaceTo(aProgress);
12324
12325 return S_OK;
12326}
12327
12328/**
12329 * @note Locks this object for writing.
12330 */
12331STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12332{
12333 LogFlowThisFuncEnter();
12334
12335 AutoCaller autoCaller(this);
12336 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12337
12338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12339
12340 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12341 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12342 && mConsoleTaskData.mLastState != MachineState_Null,
12343 E_FAIL);
12344
12345 /*
12346 * On failure, set the state to the state we had when BeginPoweringDown()
12347 * was called (this is expected by Console::PowerDown() and the associated
12348 * task). On success the VM process already changed the state to
12349 * MachineState_PoweredOff, so no need to do anything.
12350 */
12351 if (FAILED(iResult))
12352 setMachineState(mConsoleTaskData.mLastState);
12353
12354 /* notify the progress object about operation completion */
12355 Assert(mConsoleTaskData.mProgress);
12356 if (SUCCEEDED(iResult))
12357 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12358 else
12359 {
12360 Utf8Str strErrMsg(aErrMsg);
12361 if (strErrMsg.length())
12362 mConsoleTaskData.mProgress->notifyComplete(iResult,
12363 COM_IIDOF(ISession),
12364 getComponentName(),
12365 strErrMsg.c_str());
12366 else
12367 mConsoleTaskData.mProgress->notifyComplete(iResult);
12368 }
12369
12370 /* clear out the temporary saved state data */
12371 mConsoleTaskData.mLastState = MachineState_Null;
12372 mConsoleTaskData.mProgress.setNull();
12373
12374 LogFlowThisFuncLeave();
12375 return S_OK;
12376}
12377
12378
12379/**
12380 * Goes through the USB filters of the given machine to see if the given
12381 * device matches any filter or not.
12382 *
12383 * @note Locks the same as USBController::hasMatchingFilter() does.
12384 */
12385STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12386 BOOL *aMatched,
12387 ULONG *aMaskedIfs)
12388{
12389 LogFlowThisFunc(("\n"));
12390
12391 CheckComArgNotNull(aUSBDevice);
12392 CheckComArgOutPointerValid(aMatched);
12393
12394 AutoCaller autoCaller(this);
12395 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12396
12397#ifdef VBOX_WITH_USB
12398 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12399#else
12400 NOREF(aUSBDevice);
12401 NOREF(aMaskedIfs);
12402 *aMatched = FALSE;
12403#endif
12404
12405 return S_OK;
12406}
12407
12408/**
12409 * @note Locks the same as Host::captureUSBDevice() does.
12410 */
12411STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12412{
12413 LogFlowThisFunc(("\n"));
12414
12415 AutoCaller autoCaller(this);
12416 AssertComRCReturnRC(autoCaller.rc());
12417
12418#ifdef VBOX_WITH_USB
12419 /* if captureDeviceForVM() fails, it must have set extended error info */
12420 clearError();
12421 MultiResult rc = mParent->host()->checkUSBProxyService();
12422 if (FAILED(rc)) return rc;
12423
12424 USBProxyService *service = mParent->host()->usbProxyService();
12425 AssertReturn(service, E_FAIL);
12426 return service->captureDeviceForVM(this, Guid(aId).ref());
12427#else
12428 NOREF(aId);
12429 return E_NOTIMPL;
12430#endif
12431}
12432
12433/**
12434 * @note Locks the same as Host::detachUSBDevice() does.
12435 */
12436STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12437{
12438 LogFlowThisFunc(("\n"));
12439
12440 AutoCaller autoCaller(this);
12441 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12442
12443#ifdef VBOX_WITH_USB
12444 USBProxyService *service = mParent->host()->usbProxyService();
12445 AssertReturn(service, E_FAIL);
12446 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12447#else
12448 NOREF(aId);
12449 NOREF(aDone);
12450 return E_NOTIMPL;
12451#endif
12452}
12453
12454/**
12455 * Inserts all machine filters to the USB proxy service and then calls
12456 * Host::autoCaptureUSBDevices().
12457 *
12458 * Called by Console from the VM process upon VM startup.
12459 *
12460 * @note Locks what called methods lock.
12461 */
12462STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12463{
12464 LogFlowThisFunc(("\n"));
12465
12466 AutoCaller autoCaller(this);
12467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12468
12469#ifdef VBOX_WITH_USB
12470 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12471 AssertComRC(rc);
12472 NOREF(rc);
12473
12474 USBProxyService *service = mParent->host()->usbProxyService();
12475 AssertReturn(service, E_FAIL);
12476 return service->autoCaptureDevicesForVM(this);
12477#else
12478 return S_OK;
12479#endif
12480}
12481
12482/**
12483 * Removes all machine filters from the USB proxy service and then calls
12484 * Host::detachAllUSBDevices().
12485 *
12486 * Called by Console from the VM process upon normal VM termination or by
12487 * SessionMachine::uninit() upon abnormal VM termination (from under the
12488 * Machine/SessionMachine lock).
12489 *
12490 * @note Locks what called methods lock.
12491 */
12492STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12493{
12494 LogFlowThisFunc(("\n"));
12495
12496 AutoCaller autoCaller(this);
12497 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12498
12499#ifdef VBOX_WITH_USB
12500 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12501 AssertComRC(rc);
12502 NOREF(rc);
12503
12504 USBProxyService *service = mParent->host()->usbProxyService();
12505 AssertReturn(service, E_FAIL);
12506 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12507#else
12508 NOREF(aDone);
12509 return S_OK;
12510#endif
12511}
12512
12513/**
12514 * @note Locks this object for writing.
12515 */
12516STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12517 IProgress **aProgress)
12518{
12519 LogFlowThisFuncEnter();
12520
12521 AssertReturn(aSession, E_INVALIDARG);
12522 AssertReturn(aProgress, E_INVALIDARG);
12523
12524 AutoCaller autoCaller(this);
12525
12526 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12527 /*
12528 * We don't assert below because it might happen that a non-direct session
12529 * informs us it is closed right after we've been uninitialized -- it's ok.
12530 */
12531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12532
12533 /* get IInternalSessionControl interface */
12534 ComPtr<IInternalSessionControl> control(aSession);
12535
12536 ComAssertRet(!control.isNull(), E_INVALIDARG);
12537
12538 /* Creating a Progress object requires the VirtualBox lock, and
12539 * thus locking it here is required by the lock order rules. */
12540 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12541
12542 if (control == mData->mSession.mDirectControl)
12543 {
12544 ComAssertRet(aProgress, E_POINTER);
12545
12546 /* The direct session is being normally closed by the client process
12547 * ----------------------------------------------------------------- */
12548
12549 /* go to the closing state (essential for all open*Session() calls and
12550 * for #checkForDeath()) */
12551 Assert(mData->mSession.mState == SessionState_Locked);
12552 mData->mSession.mState = SessionState_Unlocking;
12553
12554 /* set direct control to NULL to release the remote instance */
12555 mData->mSession.mDirectControl.setNull();
12556 LogFlowThisFunc(("Direct control is set to NULL\n"));
12557
12558 if (mData->mSession.mProgress)
12559 {
12560 /* finalize the progress, someone might wait if a frontend
12561 * closes the session before powering on the VM. */
12562 mData->mSession.mProgress->notifyComplete(E_FAIL,
12563 COM_IIDOF(ISession),
12564 getComponentName(),
12565 tr("The VM session was closed before any attempt to power it on"));
12566 mData->mSession.mProgress.setNull();
12567 }
12568
12569 /* Create the progress object the client will use to wait until
12570 * #checkForDeath() is called to uninitialize this session object after
12571 * it releases the IPC semaphore.
12572 * Note! Because we're "reusing" mProgress here, this must be a proxy
12573 * object just like for LaunchVMProcess. */
12574 Assert(mData->mSession.mProgress.isNull());
12575 ComObjPtr<ProgressProxy> progress;
12576 progress.createObject();
12577 ComPtr<IUnknown> pPeer(mPeer);
12578 progress->init(mParent, pPeer,
12579 Bstr(tr("Closing session")).raw(),
12580 FALSE /* aCancelable */);
12581 progress.queryInterfaceTo(aProgress);
12582 mData->mSession.mProgress = progress;
12583 }
12584 else
12585 {
12586 /* the remote session is being normally closed */
12587 Data::Session::RemoteControlList::iterator it =
12588 mData->mSession.mRemoteControls.begin();
12589 while (it != mData->mSession.mRemoteControls.end())
12590 {
12591 if (control == *it)
12592 break;
12593 ++it;
12594 }
12595 BOOL found = it != mData->mSession.mRemoteControls.end();
12596 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12597 E_INVALIDARG);
12598 // This MUST be erase(it), not remove(*it) as the latter triggers a
12599 // very nasty use after free due to the place where the value "lives".
12600 mData->mSession.mRemoteControls.erase(it);
12601 }
12602
12603 /* signal the client watcher thread, because the client is going away */
12604 mParent->updateClientWatcher();
12605
12606 LogFlowThisFuncLeave();
12607 return S_OK;
12608}
12609
12610/**
12611 * @note Locks this object for writing.
12612 */
12613STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12614{
12615 LogFlowThisFuncEnter();
12616
12617 CheckComArgOutPointerValid(aProgress);
12618 CheckComArgOutPointerValid(aStateFilePath);
12619
12620 AutoCaller autoCaller(this);
12621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12622
12623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12624
12625 AssertReturn( mData->mMachineState == MachineState_Paused
12626 && mConsoleTaskData.mLastState == MachineState_Null
12627 && mConsoleTaskData.strStateFilePath.isEmpty(),
12628 E_FAIL);
12629
12630 /* create a progress object to track operation completion */
12631 ComObjPtr<Progress> pProgress;
12632 pProgress.createObject();
12633 pProgress->init(getVirtualBox(),
12634 static_cast<IMachine *>(this) /* aInitiator */,
12635 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12636 FALSE /* aCancelable */);
12637
12638 Utf8Str strStateFilePath;
12639 /* stateFilePath is null when the machine is not running */
12640 if (mData->mMachineState == MachineState_Paused)
12641 composeSavedStateFilename(strStateFilePath);
12642
12643 /* fill in the console task data */
12644 mConsoleTaskData.mLastState = mData->mMachineState;
12645 mConsoleTaskData.strStateFilePath = strStateFilePath;
12646 mConsoleTaskData.mProgress = pProgress;
12647
12648 /* set the state to Saving (this is expected by Console::SaveState()) */
12649 setMachineState(MachineState_Saving);
12650
12651 strStateFilePath.cloneTo(aStateFilePath);
12652 pProgress.queryInterfaceTo(aProgress);
12653
12654 return S_OK;
12655}
12656
12657/**
12658 * @note Locks mParent + this object for writing.
12659 */
12660STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12661{
12662 LogFlowThisFunc(("\n"));
12663
12664 AutoCaller autoCaller(this);
12665 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12666
12667 /* endSavingState() need mParent lock */
12668 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12669
12670 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12671 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12672 && mConsoleTaskData.mLastState != MachineState_Null
12673 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12674 E_FAIL);
12675
12676 /*
12677 * On failure, set the state to the state we had when BeginSavingState()
12678 * was called (this is expected by Console::SaveState() and the associated
12679 * task). On success the VM process already changed the state to
12680 * MachineState_Saved, so no need to do anything.
12681 */
12682 if (FAILED(iResult))
12683 setMachineState(mConsoleTaskData.mLastState);
12684
12685 return endSavingState(iResult, aErrMsg);
12686}
12687
12688/**
12689 * @note Locks this object for writing.
12690 */
12691STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12692{
12693 LogFlowThisFunc(("\n"));
12694
12695 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12696
12697 AutoCaller autoCaller(this);
12698 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12699
12700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12701
12702 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12703 || mData->mMachineState == MachineState_Teleported
12704 || mData->mMachineState == MachineState_Aborted
12705 , E_FAIL); /** @todo setError. */
12706
12707 Utf8Str stateFilePathFull = aSavedStateFile;
12708 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12709 if (RT_FAILURE(vrc))
12710 return setError(VBOX_E_FILE_ERROR,
12711 tr("Invalid saved state file path '%ls' (%Rrc)"),
12712 aSavedStateFile,
12713 vrc);
12714
12715 mSSData->strStateFilePath = stateFilePathFull;
12716
12717 /* The below setMachineState() will detect the state transition and will
12718 * update the settings file */
12719
12720 return setMachineState(MachineState_Saved);
12721}
12722
12723STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12724 ComSafeArrayOut(BSTR, aValues),
12725 ComSafeArrayOut(LONG64, aTimestamps),
12726 ComSafeArrayOut(BSTR, aFlags))
12727{
12728 LogFlowThisFunc(("\n"));
12729
12730#ifdef VBOX_WITH_GUEST_PROPS
12731 using namespace guestProp;
12732
12733 AutoCaller autoCaller(this);
12734 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12735
12736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12737
12738 CheckComArgOutSafeArrayPointerValid(aNames);
12739 CheckComArgOutSafeArrayPointerValid(aValues);
12740 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12741 CheckComArgOutSafeArrayPointerValid(aFlags);
12742
12743 size_t cEntries = mHWData->mGuestProperties.size();
12744 com::SafeArray<BSTR> names(cEntries);
12745 com::SafeArray<BSTR> values(cEntries);
12746 com::SafeArray<LONG64> timestamps(cEntries);
12747 com::SafeArray<BSTR> flags(cEntries);
12748 unsigned i = 0;
12749 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12750 it != mHWData->mGuestProperties.end();
12751 ++it)
12752 {
12753 char szFlags[MAX_FLAGS_LEN + 1];
12754 it->first.cloneTo(&names[i]);
12755 it->second.strValue.cloneTo(&values[i]);
12756 timestamps[i] = it->second.mTimestamp;
12757 /* If it is NULL, keep it NULL. */
12758 if (it->second.mFlags)
12759 {
12760 writeFlags(it->second.mFlags, szFlags);
12761 Bstr(szFlags).cloneTo(&flags[i]);
12762 }
12763 else
12764 flags[i] = NULL;
12765 ++i;
12766 }
12767 names.detachTo(ComSafeArrayOutArg(aNames));
12768 values.detachTo(ComSafeArrayOutArg(aValues));
12769 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12770 flags.detachTo(ComSafeArrayOutArg(aFlags));
12771 return S_OK;
12772#else
12773 ReturnComNotImplemented();
12774#endif
12775}
12776
12777STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12778 IN_BSTR aValue,
12779 LONG64 aTimestamp,
12780 IN_BSTR aFlags)
12781{
12782 LogFlowThisFunc(("\n"));
12783
12784#ifdef VBOX_WITH_GUEST_PROPS
12785 using namespace guestProp;
12786
12787 CheckComArgStrNotEmptyOrNull(aName);
12788 CheckComArgNotNull(aValue);
12789 CheckComArgNotNull(aFlags);
12790
12791 try
12792 {
12793 /*
12794 * Convert input up front.
12795 */
12796 Utf8Str utf8Name(aName);
12797 uint32_t fFlags = NILFLAG;
12798 if (aFlags)
12799 {
12800 Utf8Str utf8Flags(aFlags);
12801 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12802 AssertRCReturn(vrc, E_INVALIDARG);
12803 }
12804
12805 /*
12806 * Now grab the object lock, validate the state and do the update.
12807 */
12808 AutoCaller autoCaller(this);
12809 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12810
12811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12812
12813 switch (mData->mMachineState)
12814 {
12815 case MachineState_Paused:
12816 case MachineState_Running:
12817 case MachineState_Teleporting:
12818 case MachineState_TeleportingPausedVM:
12819 case MachineState_LiveSnapshotting:
12820 case MachineState_DeletingSnapshotOnline:
12821 case MachineState_DeletingSnapshotPaused:
12822 case MachineState_Saving:
12823 break;
12824
12825 default:
12826#ifndef DEBUG_sunlover
12827 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12828 VBOX_E_INVALID_VM_STATE);
12829#else
12830 return VBOX_E_INVALID_VM_STATE;
12831#endif
12832 }
12833
12834 setModified(IsModified_MachineData);
12835 mHWData.backup();
12836
12837 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
12838 if (it != mHWData->mGuestProperties.end())
12839 {
12840 if (aValue != NULL)
12841 {
12842 it->second.strValue = aValue;
12843 it->second.mFlags = fFlags;
12844 it->second.mTimestamp = aTimestamp;
12845 }
12846 else
12847 mHWData->mGuestProperties.erase(it);
12848
12849 mData->mGuestPropertiesModified = TRUE;
12850 }
12851
12852 /*
12853 * Send a callback notification if appropriate
12854 */
12855 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12856 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12857 RTSTR_MAX,
12858 utf8Name.c_str(),
12859 RTSTR_MAX, NULL)
12860 )
12861 {
12862 alock.release();
12863
12864 mParent->onGuestPropertyChange(mData->mUuid,
12865 aName,
12866 aValue,
12867 aFlags);
12868 }
12869 }
12870 catch (...)
12871 {
12872 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12873 }
12874 return S_OK;
12875#else
12876 ReturnComNotImplemented();
12877#endif
12878}
12879
12880STDMETHODIMP SessionMachine::LockMedia()
12881{
12882 AutoCaller autoCaller(this);
12883 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12884
12885 AutoMultiWriteLock2 alock(this->lockHandle(),
12886 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12887
12888 AssertReturn( mData->mMachineState == MachineState_Starting
12889 || mData->mMachineState == MachineState_Restoring
12890 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
12891
12892 clearError();
12893 alock.release();
12894 return lockMedia();
12895}
12896
12897STDMETHODIMP SessionMachine::UnlockMedia()
12898{
12899 unlockMedia();
12900 return S_OK;
12901}
12902
12903STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12904 IMediumAttachment **aNewAttachment)
12905{
12906 CheckComArgNotNull(aAttachment);
12907 CheckComArgOutPointerValid(aNewAttachment);
12908
12909 AutoCaller autoCaller(this);
12910 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12911
12912 // request the host lock first, since might be calling Host methods for getting host drives;
12913 // next, protect the media tree all the while we're in here, as well as our member variables
12914 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12915 this->lockHandle(),
12916 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12917
12918 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12919
12920 Bstr ctrlName;
12921 LONG lPort;
12922 LONG lDevice;
12923 bool fTempEject;
12924 {
12925 AutoCaller autoAttachCaller(this);
12926 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12927
12928 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12929
12930 /* Need to query the details first, as the IMediumAttachment reference
12931 * might be to the original settings, which we are going to change. */
12932 ctrlName = pAttach->getControllerName();
12933 lPort = pAttach->getPort();
12934 lDevice = pAttach->getDevice();
12935 fTempEject = pAttach->getTempEject();
12936 }
12937
12938 if (!fTempEject)
12939 {
12940 /* Remember previously mounted medium. The medium before taking the
12941 * backup is not necessarily the same thing. */
12942 ComObjPtr<Medium> oldmedium;
12943 oldmedium = pAttach->getMedium();
12944
12945 setModified(IsModified_Storage);
12946 mMediaData.backup();
12947
12948 // The backup operation makes the pAttach reference point to the
12949 // old settings. Re-get the correct reference.
12950 pAttach = findAttachment(mMediaData->mAttachments,
12951 ctrlName.raw(),
12952 lPort,
12953 lDevice);
12954
12955 {
12956 AutoCaller autoAttachCaller(this);
12957 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12958
12959 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12960 if (!oldmedium.isNull())
12961 oldmedium->removeBackReference(mData->mUuid);
12962
12963 pAttach->updateMedium(NULL);
12964 pAttach->updateEjected();
12965 }
12966
12967 setModified(IsModified_Storage);
12968 }
12969 else
12970 {
12971 {
12972 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12973 pAttach->updateEjected();
12974 }
12975 }
12976
12977 pAttach.queryInterfaceTo(aNewAttachment);
12978
12979 return S_OK;
12980}
12981
12982// public methods only for internal purposes
12983/////////////////////////////////////////////////////////////////////////////
12984
12985/**
12986 * Called from the client watcher thread to check for expected or unexpected
12987 * death of the client process that has a direct session to this machine.
12988 *
12989 * On Win32 and on OS/2, this method is called only when we've got the
12990 * mutex (i.e. the client has either died or terminated normally) so it always
12991 * returns @c true (the client is terminated, the session machine is
12992 * uninitialized).
12993 *
12994 * On other platforms, the method returns @c true if the client process has
12995 * terminated normally or abnormally and the session machine was uninitialized,
12996 * and @c false if the client process is still alive.
12997 *
12998 * @note Locks this object for writing.
12999 */
13000bool SessionMachine::checkForDeath()
13001{
13002 Uninit::Reason reason;
13003 bool terminated = false;
13004
13005 /* Enclose autoCaller with a block because calling uninit() from under it
13006 * will deadlock. */
13007 {
13008 AutoCaller autoCaller(this);
13009 if (!autoCaller.isOk())
13010 {
13011 /* return true if not ready, to cause the client watcher to exclude
13012 * the corresponding session from watching */
13013 LogFlowThisFunc(("Already uninitialized!\n"));
13014 return true;
13015 }
13016
13017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13018
13019 /* Determine the reason of death: if the session state is Closing here,
13020 * everything is fine. Otherwise it means that the client did not call
13021 * OnSessionEnd() before it released the IPC semaphore. This may happen
13022 * either because the client process has abnormally terminated, or
13023 * because it simply forgot to call ISession::Close() before exiting. We
13024 * threat the latter also as an abnormal termination (see
13025 * Session::uninit() for details). */
13026 reason = mData->mSession.mState == SessionState_Unlocking ?
13027 Uninit::Normal :
13028 Uninit::Abnormal;
13029
13030#if defined(RT_OS_WINDOWS)
13031
13032 AssertMsg(mIPCSem, ("semaphore must be created"));
13033
13034 /* release the IPC mutex */
13035 ::ReleaseMutex(mIPCSem);
13036
13037 terminated = true;
13038
13039#elif defined(RT_OS_OS2)
13040
13041 AssertMsg(mIPCSem, ("semaphore must be created"));
13042
13043 /* release the IPC mutex */
13044 ::DosReleaseMutexSem(mIPCSem);
13045
13046 terminated = true;
13047
13048#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13049
13050 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13051
13052 int val = ::semctl(mIPCSem, 0, GETVAL);
13053 if (val > 0)
13054 {
13055 /* the semaphore is signaled, meaning the session is terminated */
13056 terminated = true;
13057 }
13058
13059#else
13060# error "Port me!"
13061#endif
13062
13063 } /* AutoCaller block */
13064
13065 if (terminated)
13066 uninit(reason);
13067
13068 return terminated;
13069}
13070
13071/**
13072 * @note Locks this object for reading.
13073 */
13074HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13075{
13076 LogFlowThisFunc(("\n"));
13077
13078 AutoCaller autoCaller(this);
13079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13080
13081 ComPtr<IInternalSessionControl> directControl;
13082 {
13083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13084 directControl = mData->mSession.mDirectControl;
13085 }
13086
13087 /* ignore notifications sent after #OnSessionEnd() is called */
13088 if (!directControl)
13089 return S_OK;
13090
13091 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13092}
13093
13094/**
13095 * @note Locks this object for reading.
13096 */
13097HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13098 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13099{
13100 LogFlowThisFunc(("\n"));
13101
13102 AutoCaller autoCaller(this);
13103 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13104
13105 ComPtr<IInternalSessionControl> directControl;
13106 {
13107 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13108 directControl = mData->mSession.mDirectControl;
13109 }
13110
13111 /* ignore notifications sent after #OnSessionEnd() is called */
13112 if (!directControl)
13113 return S_OK;
13114 /*
13115 * instead acting like callback we ask IVirtualBox deliver corresponding event
13116 */
13117
13118 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13119 return S_OK;
13120}
13121
13122/**
13123 * @note Locks this object for reading.
13124 */
13125HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13126{
13127 LogFlowThisFunc(("\n"));
13128
13129 AutoCaller autoCaller(this);
13130 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13131
13132 ComPtr<IInternalSessionControl> directControl;
13133 {
13134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13135 directControl = mData->mSession.mDirectControl;
13136 }
13137
13138 /* ignore notifications sent after #OnSessionEnd() is called */
13139 if (!directControl)
13140 return S_OK;
13141
13142 return directControl->OnSerialPortChange(serialPort);
13143}
13144
13145/**
13146 * @note Locks this object for reading.
13147 */
13148HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13149{
13150 LogFlowThisFunc(("\n"));
13151
13152 AutoCaller autoCaller(this);
13153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13154
13155 ComPtr<IInternalSessionControl> directControl;
13156 {
13157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13158 directControl = mData->mSession.mDirectControl;
13159 }
13160
13161 /* ignore notifications sent after #OnSessionEnd() is called */
13162 if (!directControl)
13163 return S_OK;
13164
13165 return directControl->OnParallelPortChange(parallelPort);
13166}
13167
13168/**
13169 * @note Locks this object for reading.
13170 */
13171HRESULT SessionMachine::onStorageControllerChange()
13172{
13173 LogFlowThisFunc(("\n"));
13174
13175 AutoCaller autoCaller(this);
13176 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13177
13178 ComPtr<IInternalSessionControl> directControl;
13179 {
13180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13181 directControl = mData->mSession.mDirectControl;
13182 }
13183
13184 /* ignore notifications sent after #OnSessionEnd() is called */
13185 if (!directControl)
13186 return S_OK;
13187
13188 return directControl->OnStorageControllerChange();
13189}
13190
13191/**
13192 * @note Locks this object for reading.
13193 */
13194HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13195{
13196 LogFlowThisFunc(("\n"));
13197
13198 AutoCaller autoCaller(this);
13199 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13200
13201 ComPtr<IInternalSessionControl> directControl;
13202 {
13203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13204 directControl = mData->mSession.mDirectControl;
13205 }
13206
13207 /* ignore notifications sent after #OnSessionEnd() is called */
13208 if (!directControl)
13209 return S_OK;
13210
13211 return directControl->OnMediumChange(aAttachment, aForce);
13212}
13213
13214/**
13215 * @note Locks this object for reading.
13216 */
13217HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13218{
13219 LogFlowThisFunc(("\n"));
13220
13221 AutoCaller autoCaller(this);
13222 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13223
13224 ComPtr<IInternalSessionControl> directControl;
13225 {
13226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13227 directControl = mData->mSession.mDirectControl;
13228 }
13229
13230 /* ignore notifications sent after #OnSessionEnd() is called */
13231 if (!directControl)
13232 return S_OK;
13233
13234 return directControl->OnCPUChange(aCPU, aRemove);
13235}
13236
13237HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13238{
13239 LogFlowThisFunc(("\n"));
13240
13241 AutoCaller autoCaller(this);
13242 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13243
13244 ComPtr<IInternalSessionControl> directControl;
13245 {
13246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13247 directControl = mData->mSession.mDirectControl;
13248 }
13249
13250 /* ignore notifications sent after #OnSessionEnd() is called */
13251 if (!directControl)
13252 return S_OK;
13253
13254 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13255}
13256
13257/**
13258 * @note Locks this object for reading.
13259 */
13260HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13261{
13262 LogFlowThisFunc(("\n"));
13263
13264 AutoCaller autoCaller(this);
13265 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13266
13267 ComPtr<IInternalSessionControl> directControl;
13268 {
13269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13270 directControl = mData->mSession.mDirectControl;
13271 }
13272
13273 /* ignore notifications sent after #OnSessionEnd() is called */
13274 if (!directControl)
13275 return S_OK;
13276
13277 return directControl->OnVRDEServerChange(aRestart);
13278}
13279
13280/**
13281 * @note Locks this object for reading.
13282 */
13283HRESULT SessionMachine::onUSBControllerChange()
13284{
13285 LogFlowThisFunc(("\n"));
13286
13287 AutoCaller autoCaller(this);
13288 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13289
13290 ComPtr<IInternalSessionControl> directControl;
13291 {
13292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13293 directControl = mData->mSession.mDirectControl;
13294 }
13295
13296 /* ignore notifications sent after #OnSessionEnd() is called */
13297 if (!directControl)
13298 return S_OK;
13299
13300 return directControl->OnUSBControllerChange();
13301}
13302
13303/**
13304 * @note Locks this object for reading.
13305 */
13306HRESULT SessionMachine::onSharedFolderChange()
13307{
13308 LogFlowThisFunc(("\n"));
13309
13310 AutoCaller autoCaller(this);
13311 AssertComRCReturnRC(autoCaller.rc());
13312
13313 ComPtr<IInternalSessionControl> directControl;
13314 {
13315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13316 directControl = mData->mSession.mDirectControl;
13317 }
13318
13319 /* ignore notifications sent after #OnSessionEnd() is called */
13320 if (!directControl)
13321 return S_OK;
13322
13323 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13324}
13325
13326/**
13327 * @note Locks this object for reading.
13328 */
13329HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13330{
13331 LogFlowThisFunc(("\n"));
13332
13333 AutoCaller autoCaller(this);
13334 AssertComRCReturnRC(autoCaller.rc());
13335
13336 ComPtr<IInternalSessionControl> directControl;
13337 {
13338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13339 directControl = mData->mSession.mDirectControl;
13340 }
13341
13342 /* ignore notifications sent after #OnSessionEnd() is called */
13343 if (!directControl)
13344 return S_OK;
13345
13346 return directControl->OnClipboardModeChange(aClipboardMode);
13347}
13348
13349/**
13350 * @note Locks this object for reading.
13351 */
13352HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13353{
13354 LogFlowThisFunc(("\n"));
13355
13356 AutoCaller autoCaller(this);
13357 AssertComRCReturnRC(autoCaller.rc());
13358
13359 ComPtr<IInternalSessionControl> directControl;
13360 {
13361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13362 directControl = mData->mSession.mDirectControl;
13363 }
13364
13365 /* ignore notifications sent after #OnSessionEnd() is called */
13366 if (!directControl)
13367 return S_OK;
13368
13369 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13370}
13371
13372/**
13373 * @note Locks this object for reading.
13374 */
13375HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13376{
13377 LogFlowThisFunc(("\n"));
13378
13379 AutoCaller autoCaller(this);
13380 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13381
13382 ComPtr<IInternalSessionControl> directControl;
13383 {
13384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13385 directControl = mData->mSession.mDirectControl;
13386 }
13387
13388 /* ignore notifications sent after #OnSessionEnd() is called */
13389 if (!directControl)
13390 return S_OK;
13391
13392 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13393}
13394
13395/**
13396 * @note Locks this object for reading.
13397 */
13398HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13399{
13400 LogFlowThisFunc(("\n"));
13401
13402 AutoCaller autoCaller(this);
13403 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13404
13405 ComPtr<IInternalSessionControl> directControl;
13406 {
13407 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13408 directControl = mData->mSession.mDirectControl;
13409 }
13410
13411 /* ignore notifications sent after #OnSessionEnd() is called */
13412 if (!directControl)
13413 return S_OK;
13414
13415 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13416}
13417
13418/**
13419 * Returns @c true if this machine's USB controller reports it has a matching
13420 * filter for the given USB device and @c false otherwise.
13421 *
13422 * @note locks this object for reading.
13423 */
13424bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13425{
13426 AutoCaller autoCaller(this);
13427 /* silently return if not ready -- this method may be called after the
13428 * direct machine session has been called */
13429 if (!autoCaller.isOk())
13430 return false;
13431
13432#ifdef VBOX_WITH_USB
13433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13434
13435 switch (mData->mMachineState)
13436 {
13437 case MachineState_Starting:
13438 case MachineState_Restoring:
13439 case MachineState_TeleportingIn:
13440 case MachineState_Paused:
13441 case MachineState_Running:
13442 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13443 * elsewhere... */
13444 alock.release();
13445 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13446 default: break;
13447 }
13448#else
13449 NOREF(aDevice);
13450 NOREF(aMaskedIfs);
13451#endif
13452 return false;
13453}
13454
13455/**
13456 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13457 */
13458HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13459 IVirtualBoxErrorInfo *aError,
13460 ULONG aMaskedIfs)
13461{
13462 LogFlowThisFunc(("\n"));
13463
13464 AutoCaller autoCaller(this);
13465
13466 /* This notification may happen after the machine object has been
13467 * uninitialized (the session was closed), so don't assert. */
13468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13469
13470 ComPtr<IInternalSessionControl> directControl;
13471 {
13472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13473 directControl = mData->mSession.mDirectControl;
13474 }
13475
13476 /* fail on notifications sent after #OnSessionEnd() is called, it is
13477 * expected by the caller */
13478 if (!directControl)
13479 return E_FAIL;
13480
13481 /* No locks should be held at this point. */
13482 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13483 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13484
13485 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13486}
13487
13488/**
13489 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13490 */
13491HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13492 IVirtualBoxErrorInfo *aError)
13493{
13494 LogFlowThisFunc(("\n"));
13495
13496 AutoCaller autoCaller(this);
13497
13498 /* This notification may happen after the machine object has been
13499 * uninitialized (the session was closed), so don't assert. */
13500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13501
13502 ComPtr<IInternalSessionControl> directControl;
13503 {
13504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13505 directControl = mData->mSession.mDirectControl;
13506 }
13507
13508 /* fail on notifications sent after #OnSessionEnd() is called, it is
13509 * expected by the caller */
13510 if (!directControl)
13511 return E_FAIL;
13512
13513 /* No locks should be held at this point. */
13514 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13515 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13516
13517 return directControl->OnUSBDeviceDetach(aId, aError);
13518}
13519
13520// protected methods
13521/////////////////////////////////////////////////////////////////////////////
13522
13523/**
13524 * Helper method to finalize saving the state.
13525 *
13526 * @note Must be called from under this object's lock.
13527 *
13528 * @param aRc S_OK if the snapshot has been taken successfully
13529 * @param aErrMsg human readable error message for failure
13530 *
13531 * @note Locks mParent + this objects for writing.
13532 */
13533HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13534{
13535 LogFlowThisFuncEnter();
13536
13537 AutoCaller autoCaller(this);
13538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13539
13540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13541
13542 HRESULT rc = S_OK;
13543
13544 if (SUCCEEDED(aRc))
13545 {
13546 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13547
13548 /* save all VM settings */
13549 rc = saveSettings(NULL);
13550 // no need to check whether VirtualBox.xml needs saving also since
13551 // we can't have a name change pending at this point
13552 }
13553 else
13554 {
13555 // delete the saved state file (it might have been already created);
13556 // we need not check whether this is shared with a snapshot here because
13557 // we certainly created this saved state file here anew
13558 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13559 }
13560
13561 /* notify the progress object about operation completion */
13562 Assert(mConsoleTaskData.mProgress);
13563 if (SUCCEEDED(aRc))
13564 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13565 else
13566 {
13567 if (aErrMsg.length())
13568 mConsoleTaskData.mProgress->notifyComplete(aRc,
13569 COM_IIDOF(ISession),
13570 getComponentName(),
13571 aErrMsg.c_str());
13572 else
13573 mConsoleTaskData.mProgress->notifyComplete(aRc);
13574 }
13575
13576 /* clear out the temporary saved state data */
13577 mConsoleTaskData.mLastState = MachineState_Null;
13578 mConsoleTaskData.strStateFilePath.setNull();
13579 mConsoleTaskData.mProgress.setNull();
13580
13581 LogFlowThisFuncLeave();
13582 return rc;
13583}
13584
13585/**
13586 * Deletes the given file if it is no longer in use by either the current machine state
13587 * (if the machine is "saved") or any of the machine's snapshots.
13588 *
13589 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13590 * but is different for each SnapshotMachine. When calling this, the order of calling this
13591 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13592 * is therefore critical. I know, it's all rather messy.
13593 *
13594 * @param strStateFile
13595 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13596 */
13597void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13598 Snapshot *pSnapshotToIgnore)
13599{
13600 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13601 if ( (strStateFile.isNotEmpty())
13602 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13603 )
13604 // ... and it must also not be shared with other snapshots
13605 if ( !mData->mFirstSnapshot
13606 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13607 // this checks the SnapshotMachine's state file paths
13608 )
13609 RTFileDelete(strStateFile.c_str());
13610}
13611
13612/**
13613 * Locks the attached media.
13614 *
13615 * All attached hard disks are locked for writing and DVD/floppy are locked for
13616 * reading. Parents of attached hard disks (if any) are locked for reading.
13617 *
13618 * This method also performs accessibility check of all media it locks: if some
13619 * media is inaccessible, the method will return a failure and a bunch of
13620 * extended error info objects per each inaccessible medium.
13621 *
13622 * Note that this method is atomic: if it returns a success, all media are
13623 * locked as described above; on failure no media is locked at all (all
13624 * succeeded individual locks will be undone).
13625 *
13626 * The caller is responsible for doing the necessary state sanity checks.
13627 *
13628 * The locks made by this method must be undone by calling #unlockMedia() when
13629 * no more needed.
13630 */
13631HRESULT SessionMachine::lockMedia()
13632{
13633 AutoCaller autoCaller(this);
13634 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13635
13636 AutoMultiWriteLock2 alock(this->lockHandle(),
13637 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13638
13639 /* bail out if trying to lock things with already set up locking */
13640 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13641
13642 MultiResult mrc(S_OK);
13643
13644 /* Collect locking information for all medium objects attached to the VM. */
13645 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13646 it != mMediaData->mAttachments.end();
13647 ++it)
13648 {
13649 MediumAttachment* pAtt = *it;
13650 DeviceType_T devType = pAtt->getType();
13651 Medium *pMedium = pAtt->getMedium();
13652
13653 MediumLockList *pMediumLockList(new MediumLockList());
13654 // There can be attachments without a medium (floppy/dvd), and thus
13655 // it's impossible to create a medium lock list. It still makes sense
13656 // to have the empty medium lock list in the map in case a medium is
13657 // attached later.
13658 if (pMedium != NULL)
13659 {
13660 MediumType_T mediumType = pMedium->getType();
13661 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13662 || mediumType == MediumType_Shareable;
13663 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13664
13665 alock.release();
13666 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13667 !fIsReadOnlyLock /* fMediumLockWrite */,
13668 NULL,
13669 *pMediumLockList);
13670 alock.acquire();
13671 if (FAILED(mrc))
13672 {
13673 delete pMediumLockList;
13674 mData->mSession.mLockedMedia.Clear();
13675 break;
13676 }
13677 }
13678
13679 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13680 if (FAILED(rc))
13681 {
13682 mData->mSession.mLockedMedia.Clear();
13683 mrc = setError(rc,
13684 tr("Collecting locking information for all attached media failed"));
13685 break;
13686 }
13687 }
13688
13689 if (SUCCEEDED(mrc))
13690 {
13691 /* Now lock all media. If this fails, nothing is locked. */
13692 alock.release();
13693 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13694 alock.acquire();
13695 if (FAILED(rc))
13696 {
13697 mrc = setError(rc,
13698 tr("Locking of attached media failed"));
13699 }
13700 }
13701
13702 return mrc;
13703}
13704
13705/**
13706 * Undoes the locks made by by #lockMedia().
13707 */
13708void SessionMachine::unlockMedia()
13709{
13710 AutoCaller autoCaller(this);
13711 AssertComRCReturnVoid(autoCaller.rc());
13712
13713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13714
13715 /* we may be holding important error info on the current thread;
13716 * preserve it */
13717 ErrorInfoKeeper eik;
13718
13719 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13720 AssertComRC(rc);
13721}
13722
13723/**
13724 * Helper to change the machine state (reimplementation).
13725 *
13726 * @note Locks this object for writing.
13727 * @note This method must not call saveSettings or SaveSettings, otherwise
13728 * it can cause crashes in random places due to unexpectedly committing
13729 * the current settings. The caller is responsible for that. The call
13730 * to saveStateSettings is fine, because this method does not commit.
13731 */
13732HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13733{
13734 LogFlowThisFuncEnter();
13735 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13736
13737 AutoCaller autoCaller(this);
13738 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13739
13740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13741
13742 MachineState_T oldMachineState = mData->mMachineState;
13743
13744 AssertMsgReturn(oldMachineState != aMachineState,
13745 ("oldMachineState=%s, aMachineState=%s\n",
13746 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13747 E_FAIL);
13748
13749 HRESULT rc = S_OK;
13750
13751 int stsFlags = 0;
13752 bool deleteSavedState = false;
13753
13754 /* detect some state transitions */
13755
13756 if ( ( oldMachineState == MachineState_Saved
13757 && aMachineState == MachineState_Restoring)
13758 || ( ( oldMachineState == MachineState_PoweredOff
13759 || oldMachineState == MachineState_Teleported
13760 || oldMachineState == MachineState_Aborted
13761 )
13762 && ( aMachineState == MachineState_TeleportingIn
13763 || aMachineState == MachineState_Starting
13764 )
13765 )
13766 )
13767 {
13768 /* The EMT thread is about to start */
13769
13770 /* Nothing to do here for now... */
13771
13772 /// @todo NEWMEDIA don't let mDVDDrive and other children
13773 /// change anything when in the Starting/Restoring state
13774 }
13775 else if ( ( oldMachineState == MachineState_Running
13776 || oldMachineState == MachineState_Paused
13777 || oldMachineState == MachineState_Teleporting
13778 || oldMachineState == MachineState_LiveSnapshotting
13779 || oldMachineState == MachineState_Stuck
13780 || oldMachineState == MachineState_Starting
13781 || oldMachineState == MachineState_Stopping
13782 || oldMachineState == MachineState_Saving
13783 || oldMachineState == MachineState_Restoring
13784 || oldMachineState == MachineState_TeleportingPausedVM
13785 || oldMachineState == MachineState_TeleportingIn
13786 )
13787 && ( aMachineState == MachineState_PoweredOff
13788 || aMachineState == MachineState_Saved
13789 || aMachineState == MachineState_Teleported
13790 || aMachineState == MachineState_Aborted
13791 )
13792 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13793 * snapshot */
13794 && ( mConsoleTaskData.mSnapshot.isNull()
13795 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13796 )
13797 )
13798 {
13799 /* The EMT thread has just stopped, unlock attached media. Note that as
13800 * opposed to locking that is done from Console, we do unlocking here
13801 * because the VM process may have aborted before having a chance to
13802 * properly unlock all media it locked. */
13803
13804 unlockMedia();
13805 }
13806
13807 if (oldMachineState == MachineState_Restoring)
13808 {
13809 if (aMachineState != MachineState_Saved)
13810 {
13811 /*
13812 * delete the saved state file once the machine has finished
13813 * restoring from it (note that Console sets the state from
13814 * Restoring to Saved if the VM couldn't restore successfully,
13815 * to give the user an ability to fix an error and retry --
13816 * we keep the saved state file in this case)
13817 */
13818 deleteSavedState = true;
13819 }
13820 }
13821 else if ( oldMachineState == MachineState_Saved
13822 && ( aMachineState == MachineState_PoweredOff
13823 || aMachineState == MachineState_Aborted
13824 || aMachineState == MachineState_Teleported
13825 )
13826 )
13827 {
13828 /*
13829 * delete the saved state after Console::ForgetSavedState() is called
13830 * or if the VM process (owning a direct VM session) crashed while the
13831 * VM was Saved
13832 */
13833
13834 /// @todo (dmik)
13835 // Not sure that deleting the saved state file just because of the
13836 // client death before it attempted to restore the VM is a good
13837 // thing. But when it crashes we need to go to the Aborted state
13838 // which cannot have the saved state file associated... The only
13839 // way to fix this is to make the Aborted condition not a VM state
13840 // but a bool flag: i.e., when a crash occurs, set it to true and
13841 // change the state to PoweredOff or Saved depending on the
13842 // saved state presence.
13843
13844 deleteSavedState = true;
13845 mData->mCurrentStateModified = TRUE;
13846 stsFlags |= SaveSTS_CurStateModified;
13847 }
13848
13849 if ( aMachineState == MachineState_Starting
13850 || aMachineState == MachineState_Restoring
13851 || aMachineState == MachineState_TeleportingIn
13852 )
13853 {
13854 /* set the current state modified flag to indicate that the current
13855 * state is no more identical to the state in the
13856 * current snapshot */
13857 if (!mData->mCurrentSnapshot.isNull())
13858 {
13859 mData->mCurrentStateModified = TRUE;
13860 stsFlags |= SaveSTS_CurStateModified;
13861 }
13862 }
13863
13864 if (deleteSavedState)
13865 {
13866 if (mRemoveSavedState)
13867 {
13868 Assert(!mSSData->strStateFilePath.isEmpty());
13869
13870 // it is safe to delete the saved state file if ...
13871 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13872 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13873 // ... none of the snapshots share the saved state file
13874 )
13875 RTFileDelete(mSSData->strStateFilePath.c_str());
13876 }
13877
13878 mSSData->strStateFilePath.setNull();
13879 stsFlags |= SaveSTS_StateFilePath;
13880 }
13881
13882 /* redirect to the underlying peer machine */
13883 mPeer->setMachineState(aMachineState);
13884
13885 if ( aMachineState == MachineState_PoweredOff
13886 || aMachineState == MachineState_Teleported
13887 || aMachineState == MachineState_Aborted
13888 || aMachineState == MachineState_Saved)
13889 {
13890 /* the machine has stopped execution
13891 * (or the saved state file was adopted) */
13892 stsFlags |= SaveSTS_StateTimeStamp;
13893 }
13894
13895 if ( ( oldMachineState == MachineState_PoweredOff
13896 || oldMachineState == MachineState_Aborted
13897 || oldMachineState == MachineState_Teleported
13898 )
13899 && aMachineState == MachineState_Saved)
13900 {
13901 /* the saved state file was adopted */
13902 Assert(!mSSData->strStateFilePath.isEmpty());
13903 stsFlags |= SaveSTS_StateFilePath;
13904 }
13905
13906#ifdef VBOX_WITH_GUEST_PROPS
13907 if ( aMachineState == MachineState_PoweredOff
13908 || aMachineState == MachineState_Aborted
13909 || aMachineState == MachineState_Teleported)
13910 {
13911 /* Make sure any transient guest properties get removed from the
13912 * property store on shutdown. */
13913
13914 HWData::GuestPropertyMap::const_iterator it;
13915 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13916 if (!fNeedsSaving)
13917 for (it = mHWData->mGuestProperties.begin();
13918 it != mHWData->mGuestProperties.end(); ++it)
13919 if ( (it->second.mFlags & guestProp::TRANSIENT)
13920 || (it->second.mFlags & guestProp::TRANSRESET))
13921 {
13922 fNeedsSaving = true;
13923 break;
13924 }
13925 if (fNeedsSaving)
13926 {
13927 mData->mCurrentStateModified = TRUE;
13928 stsFlags |= SaveSTS_CurStateModified;
13929 }
13930 }
13931#endif
13932
13933 rc = saveStateSettings(stsFlags);
13934
13935 if ( ( oldMachineState != MachineState_PoweredOff
13936 && oldMachineState != MachineState_Aborted
13937 && oldMachineState != MachineState_Teleported
13938 )
13939 && ( aMachineState == MachineState_PoweredOff
13940 || aMachineState == MachineState_Aborted
13941 || aMachineState == MachineState_Teleported
13942 )
13943 )
13944 {
13945 /* we've been shut down for any reason */
13946 /* no special action so far */
13947 }
13948
13949 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13950 LogFlowThisFuncLeave();
13951 return rc;
13952}
13953
13954/**
13955 * Sends the current machine state value to the VM process.
13956 *
13957 * @note Locks this object for reading, then calls a client process.
13958 */
13959HRESULT SessionMachine::updateMachineStateOnClient()
13960{
13961 AutoCaller autoCaller(this);
13962 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13963
13964 ComPtr<IInternalSessionControl> directControl;
13965 {
13966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13967 AssertReturn(!!mData, E_FAIL);
13968 directControl = mData->mSession.mDirectControl;
13969
13970 /* directControl may be already set to NULL here in #OnSessionEnd()
13971 * called too early by the direct session process while there is still
13972 * some operation (like deleting the snapshot) in progress. The client
13973 * process in this case is waiting inside Session::close() for the
13974 * "end session" process object to complete, while #uninit() called by
13975 * #checkForDeath() on the Watcher thread is waiting for the pending
13976 * operation to complete. For now, we accept this inconsistent behavior
13977 * and simply do nothing here. */
13978
13979 if (mData->mSession.mState == SessionState_Unlocking)
13980 return S_OK;
13981
13982 AssertReturn(!directControl.isNull(), E_FAIL);
13983 }
13984
13985 return directControl->UpdateMachineState(mData->mMachineState);
13986}
Note: See TracBrowser for help on using the repository browser.

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