VirtualBox

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

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

Main: make the code more readable. Places where the comparisons were of the form 'isValid() == true\false' or 'isZero() == true\false' were carved \ simplified.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 470.0 KB
Line 
1/* $Id: MachineImpl.cpp 44091 2012-12-11 13:34:23Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = true;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine()
247 : mCollectorGuest(NULL),
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 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::GuestPropertyList::const_iterator it;
5447
5448 for (it = mHWData->mGuestProperties.begin();
5449 it != mHWData->mGuestProperties.end(); ++it)
5450 {
5451 if (it->strName == strName)
5452 {
5453 char szFlags[MAX_FLAGS_LEN + 1];
5454 it->strValue.cloneTo(aValue);
5455 *aTimestamp = it->mTimestamp;
5456 writeFlags(it->mFlags, szFlags);
5457 Bstr(szFlags).cloneTo(aFlags);
5458 break;
5459 }
5460 }
5461 return S_OK;
5462}
5463
5464/**
5465 * Query the VM that a guest property belongs to for the property.
5466 * @returns E_ACCESSDENIED if the VM process is not available or not
5467 * currently handling queries and the lookup should then be done in
5468 * VBoxSVC.
5469 */
5470HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5471 BSTR *aValue,
5472 LONG64 *aTimestamp,
5473 BSTR *aFlags) const
5474{
5475 HRESULT rc;
5476 ComPtr<IInternalSessionControl> directControl;
5477 directControl = mData->mSession.mDirectControl;
5478
5479 /* fail if we were called after #OnSessionEnd() is called. This is a
5480 * silly race condition. */
5481
5482 if (!directControl)
5483 rc = E_ACCESSDENIED;
5484 else
5485 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5486 false /* isSetter */,
5487 aValue, aTimestamp, aFlags);
5488 return rc;
5489}
5490#endif // VBOX_WITH_GUEST_PROPS
5491
5492STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5493 BSTR *aValue,
5494 LONG64 *aTimestamp,
5495 BSTR *aFlags)
5496{
5497#ifndef VBOX_WITH_GUEST_PROPS
5498 ReturnComNotImplemented();
5499#else // VBOX_WITH_GUEST_PROPS
5500 CheckComArgStrNotEmptyOrNull(aName);
5501 CheckComArgOutPointerValid(aValue);
5502 CheckComArgOutPointerValid(aTimestamp);
5503 CheckComArgOutPointerValid(aFlags);
5504
5505 AutoCaller autoCaller(this);
5506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5507
5508 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5509 if (rc == E_ACCESSDENIED)
5510 /* The VM is not running or the service is not (yet) accessible */
5511 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5512 return rc;
5513#endif // VBOX_WITH_GUEST_PROPS
5514}
5515
5516STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5517{
5518 LONG64 dummyTimestamp;
5519 Bstr dummyFlags;
5520 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5521}
5522
5523STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5524{
5525 Bstr dummyValue;
5526 Bstr dummyFlags;
5527 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5528}
5529
5530#ifdef VBOX_WITH_GUEST_PROPS
5531/**
5532 * Set a guest property in VBoxSVC's internal structures.
5533 */
5534HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5535 IN_BSTR aFlags)
5536{
5537 using namespace guestProp;
5538
5539 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5540 HRESULT rc = S_OK;
5541 HWData::GuestProperty property;
5542 property.mFlags = NILFLAG;
5543 bool found = false;
5544
5545 rc = checkStateDependency(MutableStateDep);
5546 if (FAILED(rc)) return rc;
5547
5548 try
5549 {
5550 Utf8Str utf8Name(aName);
5551 Utf8Str utf8Flags(aFlags);
5552 uint32_t fFlags = NILFLAG;
5553 if ( (aFlags != NULL)
5554 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5555 )
5556 return setError(E_INVALIDARG,
5557 tr("Invalid flag values: '%ls'"),
5558 aFlags);
5559
5560 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5561 * know, this is simple and do an OK job atm.) */
5562 HWData::GuestPropertyList::iterator it;
5563 for (it = mHWData->mGuestProperties.begin();
5564 it != mHWData->mGuestProperties.end(); ++it)
5565 if (it->strName == utf8Name)
5566 {
5567 property = *it;
5568 if (it->mFlags & (RDONLYHOST))
5569 rc = setError(E_ACCESSDENIED,
5570 tr("The property '%ls' cannot be changed by the host"),
5571 aName);
5572 else
5573 {
5574 setModified(IsModified_MachineData);
5575 mHWData.backup(); // @todo r=dj backup in a loop?!?
5576
5577 /* The backup() operation invalidates our iterator, so
5578 * get a new one. */
5579 for (it = mHWData->mGuestProperties.begin();
5580 it->strName != utf8Name;
5581 ++it)
5582 ;
5583 mHWData->mGuestProperties.erase(it);
5584 }
5585 found = true;
5586 break;
5587 }
5588 if (found && SUCCEEDED(rc))
5589 {
5590 if (aValue)
5591 {
5592 RTTIMESPEC time;
5593 property.strValue = aValue;
5594 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5595 if (aFlags != NULL)
5596 property.mFlags = fFlags;
5597 mHWData->mGuestProperties.push_back(property);
5598 }
5599 }
5600 else if (SUCCEEDED(rc) && aValue)
5601 {
5602 RTTIMESPEC time;
5603 setModified(IsModified_MachineData);
5604 mHWData.backup();
5605 property.strName = aName;
5606 property.strValue = aValue;
5607 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5608 property.mFlags = fFlags;
5609 mHWData->mGuestProperties.push_back(property);
5610 }
5611 if ( SUCCEEDED(rc)
5612 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5613 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5614 RTSTR_MAX,
5615 utf8Name.c_str(),
5616 RTSTR_MAX,
5617 NULL)
5618 )
5619 )
5620 {
5621 /** @todo r=bird: Why aren't we leaving the lock here? The
5622 * same code in PushGuestProperty does... */
5623 mParent->onGuestPropertyChange(mData->mUuid, aName,
5624 aValue ? aValue : Bstr("").raw(),
5625 aFlags ? aFlags : Bstr("").raw());
5626 }
5627 }
5628 catch (std::bad_alloc &)
5629 {
5630 rc = E_OUTOFMEMORY;
5631 }
5632
5633 return rc;
5634}
5635
5636/**
5637 * Set a property on the VM that that property belongs to.
5638 * @returns E_ACCESSDENIED if the VM process is not available or not
5639 * currently handling queries and the setting should then be done in
5640 * VBoxSVC.
5641 */
5642HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5643 IN_BSTR aFlags)
5644{
5645 HRESULT rc;
5646
5647 try
5648 {
5649 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5650
5651 BSTR dummy = NULL; /* will not be changed (setter) */
5652 LONG64 dummy64;
5653 if (!directControl)
5654 rc = E_ACCESSDENIED;
5655 else
5656 /** @todo Fix when adding DeleteGuestProperty(),
5657 see defect. */
5658 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5659 true /* isSetter */,
5660 &dummy, &dummy64, &dummy);
5661 }
5662 catch (std::bad_alloc &)
5663 {
5664 rc = E_OUTOFMEMORY;
5665 }
5666
5667 return rc;
5668}
5669#endif // VBOX_WITH_GUEST_PROPS
5670
5671STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5672 IN_BSTR aFlags)
5673{
5674#ifndef VBOX_WITH_GUEST_PROPS
5675 ReturnComNotImplemented();
5676#else // VBOX_WITH_GUEST_PROPS
5677 CheckComArgStrNotEmptyOrNull(aName);
5678 CheckComArgMaybeNull(aFlags);
5679 CheckComArgMaybeNull(aValue);
5680
5681 AutoCaller autoCaller(this);
5682 if (FAILED(autoCaller.rc()))
5683 return autoCaller.rc();
5684
5685 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5686 if (rc == E_ACCESSDENIED)
5687 /* The VM is not running or the service is not (yet) accessible */
5688 rc = setGuestPropertyToService(aName, aValue, aFlags);
5689 return rc;
5690#endif // VBOX_WITH_GUEST_PROPS
5691}
5692
5693STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5694{
5695 return SetGuestProperty(aName, aValue, NULL);
5696}
5697
5698STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5699{
5700 return SetGuestProperty(aName, NULL, NULL);
5701}
5702
5703#ifdef VBOX_WITH_GUEST_PROPS
5704/**
5705 * Enumerate the guest properties in VBoxSVC's internal structures.
5706 */
5707HRESULT Machine::enumerateGuestPropertiesInService
5708 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5709 ComSafeArrayOut(BSTR, aValues),
5710 ComSafeArrayOut(LONG64, aTimestamps),
5711 ComSafeArrayOut(BSTR, aFlags))
5712{
5713 using namespace guestProp;
5714
5715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5716 Utf8Str strPatterns(aPatterns);
5717
5718 /*
5719 * Look for matching patterns and build up a list.
5720 */
5721 HWData::GuestPropertyList propList;
5722 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5723 it != mHWData->mGuestProperties.end();
5724 ++it)
5725 if ( strPatterns.isEmpty()
5726 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5727 RTSTR_MAX,
5728 it->strName.c_str(),
5729 RTSTR_MAX,
5730 NULL)
5731 )
5732 propList.push_back(*it);
5733
5734 /*
5735 * And build up the arrays for returning the property information.
5736 */
5737 size_t cEntries = propList.size();
5738 SafeArray<BSTR> names(cEntries);
5739 SafeArray<BSTR> values(cEntries);
5740 SafeArray<LONG64> timestamps(cEntries);
5741 SafeArray<BSTR> flags(cEntries);
5742 size_t iProp = 0;
5743 for (HWData::GuestPropertyList::iterator it = propList.begin();
5744 it != propList.end();
5745 ++it)
5746 {
5747 char szFlags[MAX_FLAGS_LEN + 1];
5748 it->strName.cloneTo(&names[iProp]);
5749 it->strValue.cloneTo(&values[iProp]);
5750 timestamps[iProp] = it->mTimestamp;
5751 writeFlags(it->mFlags, szFlags);
5752 Bstr(szFlags).cloneTo(&flags[iProp]);
5753 ++iProp;
5754 }
5755 names.detachTo(ComSafeArrayOutArg(aNames));
5756 values.detachTo(ComSafeArrayOutArg(aValues));
5757 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5758 flags.detachTo(ComSafeArrayOutArg(aFlags));
5759 return S_OK;
5760}
5761
5762/**
5763 * Enumerate the properties managed by a VM.
5764 * @returns E_ACCESSDENIED if the VM process is not available or not
5765 * currently handling queries and the setting should then be done in
5766 * VBoxSVC.
5767 */
5768HRESULT Machine::enumerateGuestPropertiesOnVM
5769 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5770 ComSafeArrayOut(BSTR, aValues),
5771 ComSafeArrayOut(LONG64, aTimestamps),
5772 ComSafeArrayOut(BSTR, aFlags))
5773{
5774 HRESULT rc;
5775 ComPtr<IInternalSessionControl> directControl;
5776 directControl = mData->mSession.mDirectControl;
5777
5778 if (!directControl)
5779 rc = E_ACCESSDENIED;
5780 else
5781 rc = directControl->EnumerateGuestProperties
5782 (aPatterns, ComSafeArrayOutArg(aNames),
5783 ComSafeArrayOutArg(aValues),
5784 ComSafeArrayOutArg(aTimestamps),
5785 ComSafeArrayOutArg(aFlags));
5786 return rc;
5787}
5788#endif // VBOX_WITH_GUEST_PROPS
5789
5790STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5791 ComSafeArrayOut(BSTR, aNames),
5792 ComSafeArrayOut(BSTR, aValues),
5793 ComSafeArrayOut(LONG64, aTimestamps),
5794 ComSafeArrayOut(BSTR, aFlags))
5795{
5796#ifndef VBOX_WITH_GUEST_PROPS
5797 ReturnComNotImplemented();
5798#else // VBOX_WITH_GUEST_PROPS
5799 CheckComArgMaybeNull(aPatterns);
5800 CheckComArgOutSafeArrayPointerValid(aNames);
5801 CheckComArgOutSafeArrayPointerValid(aValues);
5802 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5803 CheckComArgOutSafeArrayPointerValid(aFlags);
5804
5805 AutoCaller autoCaller(this);
5806 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5807
5808 HRESULT rc = enumerateGuestPropertiesOnVM
5809 (aPatterns, ComSafeArrayOutArg(aNames),
5810 ComSafeArrayOutArg(aValues),
5811 ComSafeArrayOutArg(aTimestamps),
5812 ComSafeArrayOutArg(aFlags));
5813 if (rc == E_ACCESSDENIED)
5814 /* The VM is not running or the service is not (yet) accessible */
5815 rc = enumerateGuestPropertiesInService
5816 (aPatterns, ComSafeArrayOutArg(aNames),
5817 ComSafeArrayOutArg(aValues),
5818 ComSafeArrayOutArg(aTimestamps),
5819 ComSafeArrayOutArg(aFlags));
5820 return rc;
5821#endif // VBOX_WITH_GUEST_PROPS
5822}
5823
5824STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5825 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5826{
5827 MediaData::AttachmentList atts;
5828
5829 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5830 if (FAILED(rc)) return rc;
5831
5832 SafeIfaceArray<IMediumAttachment> attachments(atts);
5833 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5834
5835 return S_OK;
5836}
5837
5838STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5839 LONG aControllerPort,
5840 LONG aDevice,
5841 IMediumAttachment **aAttachment)
5842{
5843 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5844 aControllerName, aControllerPort, aDevice));
5845
5846 CheckComArgStrNotEmptyOrNull(aControllerName);
5847 CheckComArgOutPointerValid(aAttachment);
5848
5849 AutoCaller autoCaller(this);
5850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5851
5852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5853
5854 *aAttachment = NULL;
5855
5856 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5857 aControllerName,
5858 aControllerPort,
5859 aDevice);
5860 if (pAttach.isNull())
5861 return setError(VBOX_E_OBJECT_NOT_FOUND,
5862 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5863 aDevice, aControllerPort, aControllerName);
5864
5865 pAttach.queryInterfaceTo(aAttachment);
5866
5867 return S_OK;
5868}
5869
5870STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5871 StorageBus_T aConnectionType,
5872 IStorageController **controller)
5873{
5874 CheckComArgStrNotEmptyOrNull(aName);
5875
5876 if ( (aConnectionType <= StorageBus_Null)
5877 || (aConnectionType > StorageBus_SAS))
5878 return setError(E_INVALIDARG,
5879 tr("Invalid connection type: %d"),
5880 aConnectionType);
5881
5882 AutoCaller autoCaller(this);
5883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5884
5885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5886
5887 HRESULT rc = checkStateDependency(MutableStateDep);
5888 if (FAILED(rc)) return rc;
5889
5890 /* try to find one with the name first. */
5891 ComObjPtr<StorageController> ctrl;
5892
5893 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5894 if (SUCCEEDED(rc))
5895 return setError(VBOX_E_OBJECT_IN_USE,
5896 tr("Storage controller named '%ls' already exists"),
5897 aName);
5898
5899 ctrl.createObject();
5900
5901 /* get a new instance number for the storage controller */
5902 ULONG ulInstance = 0;
5903 bool fBootable = true;
5904 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5905 it != mStorageControllers->end();
5906 ++it)
5907 {
5908 if ((*it)->getStorageBus() == aConnectionType)
5909 {
5910 ULONG ulCurInst = (*it)->getInstance();
5911
5912 if (ulCurInst >= ulInstance)
5913 ulInstance = ulCurInst + 1;
5914
5915 /* Only one controller of each type can be marked as bootable. */
5916 if ((*it)->getBootable())
5917 fBootable = false;
5918 }
5919 }
5920
5921 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5922 if (FAILED(rc)) return rc;
5923
5924 setModified(IsModified_Storage);
5925 mStorageControllers.backup();
5926 mStorageControllers->push_back(ctrl);
5927
5928 ctrl.queryInterfaceTo(controller);
5929
5930 /* inform the direct session if any */
5931 alock.release();
5932 onStorageControllerChange();
5933
5934 return S_OK;
5935}
5936
5937STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5938 IStorageController **aStorageController)
5939{
5940 CheckComArgStrNotEmptyOrNull(aName);
5941
5942 AutoCaller autoCaller(this);
5943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5944
5945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5946
5947 ComObjPtr<StorageController> ctrl;
5948
5949 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5950 if (SUCCEEDED(rc))
5951 ctrl.queryInterfaceTo(aStorageController);
5952
5953 return rc;
5954}
5955
5956STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5957 IStorageController **aStorageController)
5958{
5959 AutoCaller autoCaller(this);
5960 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5961
5962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5963
5964 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5965 it != mStorageControllers->end();
5966 ++it)
5967 {
5968 if ((*it)->getInstance() == aInstance)
5969 {
5970 (*it).queryInterfaceTo(aStorageController);
5971 return S_OK;
5972 }
5973 }
5974
5975 return setError(VBOX_E_OBJECT_NOT_FOUND,
5976 tr("Could not find a storage controller with instance number '%lu'"),
5977 aInstance);
5978}
5979
5980STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5981{
5982 AutoCaller autoCaller(this);
5983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5984
5985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5986
5987 HRESULT rc = checkStateDependency(MutableStateDep);
5988 if (FAILED(rc)) return rc;
5989
5990 ComObjPtr<StorageController> ctrl;
5991
5992 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5993 if (SUCCEEDED(rc))
5994 {
5995 /* Ensure that only one controller of each type is marked as bootable. */
5996 if (fBootable == TRUE)
5997 {
5998 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5999 it != mStorageControllers->end();
6000 ++it)
6001 {
6002 ComObjPtr<StorageController> aCtrl = (*it);
6003
6004 if ( (aCtrl->getName() != Utf8Str(aName))
6005 && aCtrl->getBootable() == TRUE
6006 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6007 && aCtrl->getControllerType() == ctrl->getControllerType())
6008 {
6009 aCtrl->setBootable(FALSE);
6010 break;
6011 }
6012 }
6013 }
6014
6015 if (SUCCEEDED(rc))
6016 {
6017 ctrl->setBootable(fBootable);
6018 setModified(IsModified_Storage);
6019 }
6020 }
6021
6022 if (SUCCEEDED(rc))
6023 {
6024 /* inform the direct session if any */
6025 alock.release();
6026 onStorageControllerChange();
6027 }
6028
6029 return rc;
6030}
6031
6032STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6033{
6034 CheckComArgStrNotEmptyOrNull(aName);
6035
6036 AutoCaller autoCaller(this);
6037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6038
6039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6040
6041 HRESULT rc = checkStateDependency(MutableStateDep);
6042 if (FAILED(rc)) return rc;
6043
6044 ComObjPtr<StorageController> ctrl;
6045 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6046 if (FAILED(rc)) return rc;
6047
6048 {
6049 /* find all attached devices to the appropriate storage controller and detach them all */
6050 // make a temporary list because detachDevice invalidates iterators into
6051 // mMediaData->mAttachments
6052 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6053
6054 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6055 it != llAttachments2.end();
6056 ++it)
6057 {
6058 MediumAttachment *pAttachTemp = *it;
6059
6060 AutoCaller localAutoCaller(pAttachTemp);
6061 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6062
6063 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6064
6065 if (pAttachTemp->getControllerName() == aName)
6066 {
6067 rc = detachDevice(pAttachTemp, alock, NULL);
6068 if (FAILED(rc)) return rc;
6069 }
6070 }
6071 }
6072
6073 /* We can remove it now. */
6074 setModified(IsModified_Storage);
6075 mStorageControllers.backup();
6076
6077 ctrl->unshare();
6078
6079 mStorageControllers->remove(ctrl);
6080
6081 /* inform the direct session if any */
6082 alock.release();
6083 onStorageControllerChange();
6084
6085 return S_OK;
6086}
6087
6088STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6089 ULONG *puOriginX,
6090 ULONG *puOriginY,
6091 ULONG *puWidth,
6092 ULONG *puHeight,
6093 BOOL *pfEnabled)
6094{
6095 LogFlowThisFunc(("\n"));
6096
6097 CheckComArgNotNull(puOriginX);
6098 CheckComArgNotNull(puOriginY);
6099 CheckComArgNotNull(puWidth);
6100 CheckComArgNotNull(puHeight);
6101 CheckComArgNotNull(pfEnabled);
6102
6103 uint32_t u32OriginX= 0;
6104 uint32_t u32OriginY= 0;
6105 uint32_t u32Width = 0;
6106 uint32_t u32Height = 0;
6107 uint16_t u16Flags = 0;
6108
6109 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6110 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6111 if (RT_FAILURE(vrc))
6112 {
6113#ifdef RT_OS_WINDOWS
6114 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6115 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6116 * So just assign fEnable to TRUE again.
6117 * The right fix would be to change GUI API wrappers to make sure that parameters
6118 * are changed only if API succeeds.
6119 */
6120 *pfEnabled = TRUE;
6121#endif
6122 return setError(VBOX_E_IPRT_ERROR,
6123 tr("Saved guest size is not available (%Rrc)"),
6124 vrc);
6125 }
6126
6127 *puOriginX = u32OriginX;
6128 *puOriginY = u32OriginY;
6129 *puWidth = u32Width;
6130 *puHeight = u32Height;
6131 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6132
6133 return S_OK;
6134}
6135
6136STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6137{
6138 LogFlowThisFunc(("\n"));
6139
6140 CheckComArgNotNull(aSize);
6141 CheckComArgNotNull(aWidth);
6142 CheckComArgNotNull(aHeight);
6143
6144 if (aScreenId != 0)
6145 return E_NOTIMPL;
6146
6147 AutoCaller autoCaller(this);
6148 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6149
6150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6151
6152 uint8_t *pu8Data = NULL;
6153 uint32_t cbData = 0;
6154 uint32_t u32Width = 0;
6155 uint32_t u32Height = 0;
6156
6157 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6158
6159 if (RT_FAILURE(vrc))
6160 return setError(VBOX_E_IPRT_ERROR,
6161 tr("Saved screenshot data is not available (%Rrc)"),
6162 vrc);
6163
6164 *aSize = cbData;
6165 *aWidth = u32Width;
6166 *aHeight = u32Height;
6167
6168 freeSavedDisplayScreenshot(pu8Data);
6169
6170 return S_OK;
6171}
6172
6173STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6174{
6175 LogFlowThisFunc(("\n"));
6176
6177 CheckComArgNotNull(aWidth);
6178 CheckComArgNotNull(aHeight);
6179 CheckComArgOutSafeArrayPointerValid(aData);
6180
6181 if (aScreenId != 0)
6182 return E_NOTIMPL;
6183
6184 AutoCaller autoCaller(this);
6185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6186
6187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6188
6189 uint8_t *pu8Data = NULL;
6190 uint32_t cbData = 0;
6191 uint32_t u32Width = 0;
6192 uint32_t u32Height = 0;
6193
6194 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6195
6196 if (RT_FAILURE(vrc))
6197 return setError(VBOX_E_IPRT_ERROR,
6198 tr("Saved screenshot data is not available (%Rrc)"),
6199 vrc);
6200
6201 *aWidth = u32Width;
6202 *aHeight = u32Height;
6203
6204 com::SafeArray<BYTE> bitmap(cbData);
6205 /* Convert pixels to format expected by the API caller. */
6206 if (aBGR)
6207 {
6208 /* [0] B, [1] G, [2] R, [3] A. */
6209 for (unsigned i = 0; i < cbData; i += 4)
6210 {
6211 bitmap[i] = pu8Data[i];
6212 bitmap[i + 1] = pu8Data[i + 1];
6213 bitmap[i + 2] = pu8Data[i + 2];
6214 bitmap[i + 3] = 0xff;
6215 }
6216 }
6217 else
6218 {
6219 /* [0] R, [1] G, [2] B, [3] A. */
6220 for (unsigned i = 0; i < cbData; i += 4)
6221 {
6222 bitmap[i] = pu8Data[i + 2];
6223 bitmap[i + 1] = pu8Data[i + 1];
6224 bitmap[i + 2] = pu8Data[i];
6225 bitmap[i + 3] = 0xff;
6226 }
6227 }
6228 bitmap.detachTo(ComSafeArrayOutArg(aData));
6229
6230 freeSavedDisplayScreenshot(pu8Data);
6231
6232 return S_OK;
6233}
6234
6235
6236STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6237{
6238 LogFlowThisFunc(("\n"));
6239
6240 CheckComArgNotNull(aWidth);
6241 CheckComArgNotNull(aHeight);
6242 CheckComArgOutSafeArrayPointerValid(aData);
6243
6244 if (aScreenId != 0)
6245 return E_NOTIMPL;
6246
6247 AutoCaller autoCaller(this);
6248 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6249
6250 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6251
6252 uint8_t *pu8Data = NULL;
6253 uint32_t cbData = 0;
6254 uint32_t u32Width = 0;
6255 uint32_t u32Height = 0;
6256
6257 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6258
6259 if (RT_FAILURE(vrc))
6260 return setError(VBOX_E_IPRT_ERROR,
6261 tr("Saved screenshot data is not available (%Rrc)"),
6262 vrc);
6263
6264 *aWidth = u32Width;
6265 *aHeight = u32Height;
6266
6267 HRESULT rc = S_OK;
6268 uint8_t *pu8PNG = NULL;
6269 uint32_t cbPNG = 0;
6270 uint32_t cxPNG = 0;
6271 uint32_t cyPNG = 0;
6272
6273 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6274
6275 if (RT_SUCCESS(vrc))
6276 {
6277 com::SafeArray<BYTE> screenData(cbPNG);
6278 screenData.initFrom(pu8PNG, cbPNG);
6279 if (pu8PNG)
6280 RTMemFree(pu8PNG);
6281 screenData.detachTo(ComSafeArrayOutArg(aData));
6282 }
6283 else
6284 {
6285 if (pu8PNG)
6286 RTMemFree(pu8PNG);
6287 return setError(VBOX_E_IPRT_ERROR,
6288 tr("Could not convert screenshot to PNG (%Rrc)"),
6289 vrc);
6290 }
6291
6292 freeSavedDisplayScreenshot(pu8Data);
6293
6294 return rc;
6295}
6296
6297STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6298{
6299 LogFlowThisFunc(("\n"));
6300
6301 CheckComArgNotNull(aSize);
6302 CheckComArgNotNull(aWidth);
6303 CheckComArgNotNull(aHeight);
6304
6305 if (aScreenId != 0)
6306 return E_NOTIMPL;
6307
6308 AutoCaller autoCaller(this);
6309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6310
6311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6312
6313 uint8_t *pu8Data = NULL;
6314 uint32_t cbData = 0;
6315 uint32_t u32Width = 0;
6316 uint32_t u32Height = 0;
6317
6318 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6319
6320 if (RT_FAILURE(vrc))
6321 return setError(VBOX_E_IPRT_ERROR,
6322 tr("Saved screenshot data is not available (%Rrc)"),
6323 vrc);
6324
6325 *aSize = cbData;
6326 *aWidth = u32Width;
6327 *aHeight = u32Height;
6328
6329 freeSavedDisplayScreenshot(pu8Data);
6330
6331 return S_OK;
6332}
6333
6334STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6335{
6336 LogFlowThisFunc(("\n"));
6337
6338 CheckComArgNotNull(aWidth);
6339 CheckComArgNotNull(aHeight);
6340 CheckComArgOutSafeArrayPointerValid(aData);
6341
6342 if (aScreenId != 0)
6343 return E_NOTIMPL;
6344
6345 AutoCaller autoCaller(this);
6346 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6347
6348 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6349
6350 uint8_t *pu8Data = NULL;
6351 uint32_t cbData = 0;
6352 uint32_t u32Width = 0;
6353 uint32_t u32Height = 0;
6354
6355 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6356
6357 if (RT_FAILURE(vrc))
6358 return setError(VBOX_E_IPRT_ERROR,
6359 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6360 vrc);
6361
6362 *aWidth = u32Width;
6363 *aHeight = u32Height;
6364
6365 com::SafeArray<BYTE> png(cbData);
6366 png.initFrom(pu8Data, cbData);
6367 png.detachTo(ComSafeArrayOutArg(aData));
6368
6369 freeSavedDisplayScreenshot(pu8Data);
6370
6371 return S_OK;
6372}
6373
6374STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6375{
6376 HRESULT rc = S_OK;
6377 LogFlowThisFunc(("\n"));
6378
6379 AutoCaller autoCaller(this);
6380 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6381
6382 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 if (!mHWData->mCPUHotPlugEnabled)
6385 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6386
6387 if (aCpu >= mHWData->mCPUCount)
6388 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6389
6390 if (mHWData->mCPUAttached[aCpu])
6391 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6392
6393 alock.release();
6394 rc = onCPUChange(aCpu, false);
6395 alock.acquire();
6396 if (FAILED(rc)) return rc;
6397
6398 setModified(IsModified_MachineData);
6399 mHWData.backup();
6400 mHWData->mCPUAttached[aCpu] = true;
6401
6402 /* Save settings if online */
6403 if (Global::IsOnline(mData->mMachineState))
6404 saveSettings(NULL);
6405
6406 return S_OK;
6407}
6408
6409STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6410{
6411 HRESULT rc = S_OK;
6412 LogFlowThisFunc(("\n"));
6413
6414 AutoCaller autoCaller(this);
6415 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6416
6417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6418
6419 if (!mHWData->mCPUHotPlugEnabled)
6420 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6421
6422 if (aCpu >= SchemaDefs::MaxCPUCount)
6423 return setError(E_INVALIDARG,
6424 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6425 SchemaDefs::MaxCPUCount);
6426
6427 if (!mHWData->mCPUAttached[aCpu])
6428 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6429
6430 /* CPU 0 can't be detached */
6431 if (aCpu == 0)
6432 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6433
6434 alock.release();
6435 rc = onCPUChange(aCpu, true);
6436 alock.acquire();
6437 if (FAILED(rc)) return rc;
6438
6439 setModified(IsModified_MachineData);
6440 mHWData.backup();
6441 mHWData->mCPUAttached[aCpu] = false;
6442
6443 /* Save settings if online */
6444 if (Global::IsOnline(mData->mMachineState))
6445 saveSettings(NULL);
6446
6447 return S_OK;
6448}
6449
6450STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6451{
6452 LogFlowThisFunc(("\n"));
6453
6454 CheckComArgNotNull(aCpuAttached);
6455
6456 *aCpuAttached = false;
6457
6458 AutoCaller autoCaller(this);
6459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6460
6461 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6462
6463 /* If hotplug is enabled the CPU is always enabled. */
6464 if (!mHWData->mCPUHotPlugEnabled)
6465 {
6466 if (aCpu < mHWData->mCPUCount)
6467 *aCpuAttached = true;
6468 }
6469 else
6470 {
6471 if (aCpu < SchemaDefs::MaxCPUCount)
6472 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6473 }
6474
6475 return S_OK;
6476}
6477
6478STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6479{
6480 CheckComArgOutPointerValid(aName);
6481
6482 AutoCaller autoCaller(this);
6483 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6484
6485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6486
6487 Utf8Str log = queryLogFilename(aIdx);
6488 if (!RTFileExists(log.c_str()))
6489 log.setNull();
6490 log.cloneTo(aName);
6491
6492 return S_OK;
6493}
6494
6495STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6496{
6497 LogFlowThisFunc(("\n"));
6498 CheckComArgOutSafeArrayPointerValid(aData);
6499 if (aSize < 0)
6500 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6501
6502 AutoCaller autoCaller(this);
6503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6504
6505 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6506
6507 HRESULT rc = S_OK;
6508 Utf8Str log = queryLogFilename(aIdx);
6509
6510 /* do not unnecessarily hold the lock while doing something which does
6511 * not need the lock and potentially takes a long time. */
6512 alock.release();
6513
6514 /* Limit the chunk size to 32K for now, as that gives better performance
6515 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6516 * One byte expands to approx. 25 bytes of breathtaking XML. */
6517 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6518 com::SafeArray<BYTE> logData(cbData);
6519
6520 RTFILE LogFile;
6521 int vrc = RTFileOpen(&LogFile, log.c_str(),
6522 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6523 if (RT_SUCCESS(vrc))
6524 {
6525 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6526 if (RT_SUCCESS(vrc))
6527 logData.resize(cbData);
6528 else
6529 rc = setError(VBOX_E_IPRT_ERROR,
6530 tr("Could not read log file '%s' (%Rrc)"),
6531 log.c_str(), vrc);
6532 RTFileClose(LogFile);
6533 }
6534 else
6535 rc = setError(VBOX_E_IPRT_ERROR,
6536 tr("Could not open log file '%s' (%Rrc)"),
6537 log.c_str(), vrc);
6538
6539 if (FAILED(rc))
6540 logData.resize(0);
6541 logData.detachTo(ComSafeArrayOutArg(aData));
6542
6543 return rc;
6544}
6545
6546
6547/**
6548 * Currently this method doesn't attach device to the running VM,
6549 * just makes sure it's plugged on next VM start.
6550 */
6551STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6552{
6553 AutoCaller autoCaller(this);
6554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6555
6556 // lock scope
6557 {
6558 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6559
6560 HRESULT rc = checkStateDependency(MutableStateDep);
6561 if (FAILED(rc)) return rc;
6562
6563 ChipsetType_T aChipset = ChipsetType_PIIX3;
6564 COMGETTER(ChipsetType)(&aChipset);
6565
6566 if (aChipset != ChipsetType_ICH9)
6567 {
6568 return setError(E_INVALIDARG,
6569 tr("Host PCI attachment only supported with ICH9 chipset"));
6570 }
6571
6572 // check if device with this host PCI address already attached
6573 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6574 it != mHWData->mPCIDeviceAssignments.end();
6575 ++it)
6576 {
6577 LONG iHostAddress = -1;
6578 ComPtr<PCIDeviceAttachment> pAttach;
6579 pAttach = *it;
6580 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6581 if (iHostAddress == hostAddress)
6582 return setError(E_INVALIDARG,
6583 tr("Device with host PCI address already attached to this VM"));
6584 }
6585
6586 ComObjPtr<PCIDeviceAttachment> pda;
6587 char name[32];
6588
6589 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6590 Bstr bname(name);
6591 pda.createObject();
6592 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6593 setModified(IsModified_MachineData);
6594 mHWData.backup();
6595 mHWData->mPCIDeviceAssignments.push_back(pda);
6596 }
6597
6598 return S_OK;
6599}
6600
6601/**
6602 * Currently this method doesn't detach device from the running VM,
6603 * just makes sure it's not plugged on next VM start.
6604 */
6605STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6606{
6607 AutoCaller autoCaller(this);
6608 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6609
6610 ComObjPtr<PCIDeviceAttachment> pAttach;
6611 bool fRemoved = false;
6612 HRESULT rc;
6613
6614 // lock scope
6615 {
6616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6617
6618 rc = checkStateDependency(MutableStateDep);
6619 if (FAILED(rc)) return rc;
6620
6621 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6622 it != mHWData->mPCIDeviceAssignments.end();
6623 ++it)
6624 {
6625 LONG iHostAddress = -1;
6626 pAttach = *it;
6627 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6628 if (iHostAddress != -1 && iHostAddress == hostAddress)
6629 {
6630 setModified(IsModified_MachineData);
6631 mHWData.backup();
6632 mHWData->mPCIDeviceAssignments.remove(pAttach);
6633 fRemoved = true;
6634 break;
6635 }
6636 }
6637 }
6638
6639
6640 /* Fire event outside of the lock */
6641 if (fRemoved)
6642 {
6643 Assert(!pAttach.isNull());
6644 ComPtr<IEventSource> es;
6645 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6646 Assert(SUCCEEDED(rc));
6647 Bstr mid;
6648 rc = this->COMGETTER(Id)(mid.asOutParam());
6649 Assert(SUCCEEDED(rc));
6650 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6651 }
6652
6653 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6654 tr("No host PCI device %08x attached"),
6655 hostAddress
6656 );
6657}
6658
6659STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6660{
6661 CheckComArgOutSafeArrayPointerValid(aAssignments);
6662
6663 AutoCaller autoCaller(this);
6664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6665
6666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6667
6668 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6669 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6670
6671 return S_OK;
6672}
6673
6674STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6675{
6676 CheckComArgOutPointerValid(aBandwidthControl);
6677
6678 AutoCaller autoCaller(this);
6679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6680
6681 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6682
6683 return S_OK;
6684}
6685
6686STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6687{
6688 CheckComArgOutPointerValid(pfEnabled);
6689 AutoCaller autoCaller(this);
6690 HRESULT hrc = autoCaller.rc();
6691 if (SUCCEEDED(hrc))
6692 {
6693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6694 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6695 }
6696 return hrc;
6697}
6698
6699STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6700{
6701 AutoCaller autoCaller(this);
6702 HRESULT hrc = autoCaller.rc();
6703 if (SUCCEEDED(hrc))
6704 {
6705 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6706 hrc = checkStateDependency(MutableStateDep);
6707 if (SUCCEEDED(hrc))
6708 {
6709 hrc = mHWData.backupEx();
6710 if (SUCCEEDED(hrc))
6711 {
6712 setModified(IsModified_MachineData);
6713 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6714 }
6715 }
6716 }
6717 return hrc;
6718}
6719
6720STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6721{
6722 CheckComArgOutPointerValid(pbstrConfig);
6723 AutoCaller autoCaller(this);
6724 HRESULT hrc = autoCaller.rc();
6725 if (SUCCEEDED(hrc))
6726 {
6727 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6728 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6729 }
6730 return hrc;
6731}
6732
6733STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6734{
6735 CheckComArgStr(bstrConfig);
6736 AutoCaller autoCaller(this);
6737 HRESULT hrc = autoCaller.rc();
6738 if (SUCCEEDED(hrc))
6739 {
6740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6741 hrc = checkStateDependency(MutableStateDep);
6742 if (SUCCEEDED(hrc))
6743 {
6744 hrc = mHWData.backupEx();
6745 if (SUCCEEDED(hrc))
6746 {
6747 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6748 if (SUCCEEDED(hrc))
6749 setModified(IsModified_MachineData);
6750 }
6751 }
6752 }
6753 return hrc;
6754
6755}
6756
6757STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6758{
6759 CheckComArgOutPointerValid(pfAllow);
6760 AutoCaller autoCaller(this);
6761 HRESULT hrc = autoCaller.rc();
6762 if (SUCCEEDED(hrc))
6763 {
6764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6765 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6766 }
6767 return hrc;
6768}
6769
6770STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6771{
6772 AutoCaller autoCaller(this);
6773 HRESULT hrc = autoCaller.rc();
6774 if (SUCCEEDED(hrc))
6775 {
6776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6777 hrc = checkStateDependency(MutableStateDep);
6778 if (SUCCEEDED(hrc))
6779 {
6780 hrc = mHWData.backupEx();
6781 if (SUCCEEDED(hrc))
6782 {
6783 setModified(IsModified_MachineData);
6784 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6785 }
6786 }
6787 }
6788 return hrc;
6789}
6790
6791STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6792{
6793 CheckComArgOutPointerValid(pfEnabled);
6794 AutoCaller autoCaller(this);
6795 HRESULT hrc = autoCaller.rc();
6796 if (SUCCEEDED(hrc))
6797 {
6798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6799 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6800 }
6801 return hrc;
6802}
6803
6804STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6805{
6806 AutoCaller autoCaller(this);
6807 HRESULT hrc = autoCaller.rc();
6808 if (SUCCEEDED(hrc))
6809 {
6810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6811 hrc = checkStateDependency(MutableStateDep);
6812 if ( SUCCEEDED(hrc)
6813 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6814 {
6815 AutostartDb *autostartDb = mParent->getAutostartDb();
6816 int vrc;
6817
6818 if (fEnabled)
6819 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6820 else
6821 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6822
6823 if (RT_SUCCESS(vrc))
6824 {
6825 hrc = mHWData.backupEx();
6826 if (SUCCEEDED(hrc))
6827 {
6828 setModified(IsModified_MachineData);
6829 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6830 }
6831 }
6832 else if (vrc == VERR_NOT_SUPPORTED)
6833 hrc = setError(VBOX_E_NOT_SUPPORTED,
6834 tr("The VM autostart feature is not supported on this platform"));
6835 else if (vrc == VERR_PATH_NOT_FOUND)
6836 hrc = setError(E_FAIL,
6837 tr("The path to the autostart database is not set"));
6838 else
6839 hrc = setError(E_UNEXPECTED,
6840 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6841 fEnabled ? "Adding" : "Removing",
6842 mUserData->s.strName.c_str(), vrc);
6843 }
6844 }
6845 return hrc;
6846}
6847
6848STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6849{
6850 CheckComArgOutPointerValid(puDelay);
6851 AutoCaller autoCaller(this);
6852 HRESULT hrc = autoCaller.rc();
6853 if (SUCCEEDED(hrc))
6854 {
6855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 *puDelay = mHWData->mAutostart.uAutostartDelay;
6857 }
6858 return hrc;
6859}
6860
6861STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6862{
6863 AutoCaller autoCaller(this);
6864 HRESULT hrc = autoCaller.rc();
6865 if (SUCCEEDED(hrc))
6866 {
6867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6868 hrc = checkStateDependency(MutableStateDep);
6869 if (SUCCEEDED(hrc))
6870 {
6871 hrc = mHWData.backupEx();
6872 if (SUCCEEDED(hrc))
6873 {
6874 setModified(IsModified_MachineData);
6875 mHWData->mAutostart.uAutostartDelay = uDelay;
6876 }
6877 }
6878 }
6879 return hrc;
6880}
6881
6882STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6883{
6884 CheckComArgOutPointerValid(penmAutostopType);
6885 AutoCaller autoCaller(this);
6886 HRESULT hrc = autoCaller.rc();
6887 if (SUCCEEDED(hrc))
6888 {
6889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6891 }
6892 return hrc;
6893}
6894
6895STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6896{
6897 AutoCaller autoCaller(this);
6898 HRESULT hrc = autoCaller.rc();
6899 if (SUCCEEDED(hrc))
6900 {
6901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6902 hrc = checkStateDependency(MutableStateDep);
6903 if ( SUCCEEDED(hrc)
6904 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6905 {
6906 AutostartDb *autostartDb = mParent->getAutostartDb();
6907 int vrc;
6908
6909 if (enmAutostopType != AutostopType_Disabled)
6910 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6911 else
6912 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6913
6914 if (RT_SUCCESS(vrc))
6915 {
6916 hrc = mHWData.backupEx();
6917 if (SUCCEEDED(hrc))
6918 {
6919 setModified(IsModified_MachineData);
6920 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6921 }
6922 }
6923 else if (vrc == VERR_NOT_SUPPORTED)
6924 hrc = setError(VBOX_E_NOT_SUPPORTED,
6925 tr("The VM autostop feature is not supported on this platform"));
6926 else if (vrc == VERR_PATH_NOT_FOUND)
6927 hrc = setError(E_FAIL,
6928 tr("The path to the autostart database is not set"));
6929 else
6930 hrc = setError(E_UNEXPECTED,
6931 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6932 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6933 mUserData->s.strName.c_str(), vrc);
6934 }
6935 }
6936 return hrc;
6937}
6938
6939
6940STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6941{
6942 LogFlowFuncEnter();
6943
6944 CheckComArgNotNull(pTarget);
6945 CheckComArgOutPointerValid(pProgress);
6946
6947 /* Convert the options. */
6948 RTCList<CloneOptions_T> optList;
6949 if (options != NULL)
6950 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6951
6952 if (optList.contains(CloneOptions_Link))
6953 {
6954 if (!isSnapshotMachine())
6955 return setError(E_INVALIDARG,
6956 tr("Linked clone can only be created from a snapshot"));
6957 if (mode != CloneMode_MachineState)
6958 return setError(E_INVALIDARG,
6959 tr("Linked clone can only be created for a single machine state"));
6960 }
6961 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6962
6963 AutoCaller autoCaller(this);
6964 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6965
6966
6967 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6968
6969 HRESULT rc = pWorker->start(pProgress);
6970
6971 LogFlowFuncLeave();
6972
6973 return rc;
6974}
6975
6976// public methods for internal purposes
6977/////////////////////////////////////////////////////////////////////////////
6978
6979/**
6980 * Adds the given IsModified_* flag to the dirty flags of the machine.
6981 * This must be called either during loadSettings or under the machine write lock.
6982 * @param fl
6983 */
6984void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6985{
6986 mData->flModifications |= fl;
6987 if (fAllowStateModification && isStateModificationAllowed())
6988 mData->mCurrentStateModified = true;
6989}
6990
6991/**
6992 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6993 * care of the write locking.
6994 *
6995 * @param fModifications The flag to add.
6996 */
6997void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6998{
6999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7000 setModified(fModification, fAllowStateModification);
7001}
7002
7003/**
7004 * Saves the registry entry of this machine to the given configuration node.
7005 *
7006 * @param aEntryNode Node to save the registry entry to.
7007 *
7008 * @note locks this object for reading.
7009 */
7010HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7011{
7012 AutoLimitedCaller autoCaller(this);
7013 AssertComRCReturnRC(autoCaller.rc());
7014
7015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7016
7017 data.uuid = mData->mUuid;
7018 data.strSettingsFile = mData->m_strConfigFile;
7019
7020 return S_OK;
7021}
7022
7023/**
7024 * Calculates the absolute path of the given path taking the directory of the
7025 * machine settings file as the current directory.
7026 *
7027 * @param aPath Path to calculate the absolute path for.
7028 * @param aResult Where to put the result (used only on success, can be the
7029 * same Utf8Str instance as passed in @a aPath).
7030 * @return IPRT result.
7031 *
7032 * @note Locks this object for reading.
7033 */
7034int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7035{
7036 AutoCaller autoCaller(this);
7037 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7038
7039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7040
7041 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7042
7043 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7044
7045 strSettingsDir.stripFilename();
7046 char folder[RTPATH_MAX];
7047 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7048 if (RT_SUCCESS(vrc))
7049 aResult = folder;
7050
7051 return vrc;
7052}
7053
7054/**
7055 * Copies strSource to strTarget, making it relative to the machine folder
7056 * if it is a subdirectory thereof, or simply copying it otherwise.
7057 *
7058 * @param strSource Path to evaluate and copy.
7059 * @param strTarget Buffer to receive target path.
7060 *
7061 * @note Locks this object for reading.
7062 */
7063void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7064 Utf8Str &strTarget)
7065{
7066 AutoCaller autoCaller(this);
7067 AssertComRCReturn(autoCaller.rc(), (void)0);
7068
7069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7070
7071 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7072 // use strTarget as a temporary buffer to hold the machine settings dir
7073 strTarget = mData->m_strConfigFileFull;
7074 strTarget.stripFilename();
7075 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7076 {
7077 // is relative: then append what's left
7078 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7079 // for empty paths (only possible for subdirs) use "." to avoid
7080 // triggering default settings for not present config attributes.
7081 if (strTarget.isEmpty())
7082 strTarget = ".";
7083 }
7084 else
7085 // is not relative: then overwrite
7086 strTarget = strSource;
7087}
7088
7089/**
7090 * Returns the full path to the machine's log folder in the
7091 * \a aLogFolder argument.
7092 */
7093void Machine::getLogFolder(Utf8Str &aLogFolder)
7094{
7095 AutoCaller autoCaller(this);
7096 AssertComRCReturnVoid(autoCaller.rc());
7097
7098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7099
7100 char szTmp[RTPATH_MAX];
7101 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7102 if (RT_SUCCESS(vrc))
7103 {
7104 if (szTmp[0] && !mUserData.isNull())
7105 {
7106 char szTmp2[RTPATH_MAX];
7107 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7108 if (RT_SUCCESS(vrc))
7109 aLogFolder = BstrFmt("%s%c%s",
7110 szTmp2,
7111 RTPATH_DELIMITER,
7112 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7113 }
7114 else
7115 vrc = VERR_PATH_IS_RELATIVE;
7116 }
7117
7118 if (RT_FAILURE(vrc))
7119 {
7120 // fallback if VBOX_USER_LOGHOME is not set or invalid
7121 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7122 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7123 aLogFolder.append(RTPATH_DELIMITER);
7124 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7125 }
7126}
7127
7128/**
7129 * Returns the full path to the machine's log file for an given index.
7130 */
7131Utf8Str Machine::queryLogFilename(ULONG idx)
7132{
7133 Utf8Str logFolder;
7134 getLogFolder(logFolder);
7135 Assert(logFolder.length());
7136 Utf8Str log;
7137 if (idx == 0)
7138 log = Utf8StrFmt("%s%cVBox.log",
7139 logFolder.c_str(), RTPATH_DELIMITER);
7140 else
7141 log = Utf8StrFmt("%s%cVBox.log.%d",
7142 logFolder.c_str(), RTPATH_DELIMITER, idx);
7143 return log;
7144}
7145
7146/**
7147 * Composes a unique saved state filename based on the current system time. The filename is
7148 * granular to the second so this will work so long as no more than one snapshot is taken on
7149 * a machine per second.
7150 *
7151 * Before version 4.1, we used this formula for saved state files:
7152 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7153 * which no longer works because saved state files can now be shared between the saved state of the
7154 * "saved" machine and an online snapshot, and the following would cause problems:
7155 * 1) save machine
7156 * 2) create online snapshot from that machine state --> reusing saved state file
7157 * 3) save machine again --> filename would be reused, breaking the online snapshot
7158 *
7159 * So instead we now use a timestamp.
7160 *
7161 * @param str
7162 */
7163void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7164{
7165 AutoCaller autoCaller(this);
7166 AssertComRCReturnVoid(autoCaller.rc());
7167
7168 {
7169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7170 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7171 }
7172
7173 RTTIMESPEC ts;
7174 RTTimeNow(&ts);
7175 RTTIME time;
7176 RTTimeExplode(&time, &ts);
7177
7178 strStateFilePath += RTPATH_DELIMITER;
7179 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7180 time.i32Year, time.u8Month, time.u8MonthDay,
7181 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7182}
7183
7184/**
7185 * @note Locks this object for writing, calls the client process
7186 * (inside the lock).
7187 */
7188HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7189 const Utf8Str &strType,
7190 const Utf8Str &strEnvironment,
7191 ProgressProxy *aProgress)
7192{
7193 LogFlowThisFuncEnter();
7194
7195 AssertReturn(aControl, E_FAIL);
7196 AssertReturn(aProgress, E_FAIL);
7197
7198 AutoCaller autoCaller(this);
7199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7200
7201 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7202
7203 if (!mData->mRegistered)
7204 return setError(E_UNEXPECTED,
7205 tr("The machine '%s' is not registered"),
7206 mUserData->s.strName.c_str());
7207
7208 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7209
7210 if ( mData->mSession.mState == SessionState_Locked
7211 || mData->mSession.mState == SessionState_Spawning
7212 || mData->mSession.mState == SessionState_Unlocking)
7213 return setError(VBOX_E_INVALID_OBJECT_STATE,
7214 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7215 mUserData->s.strName.c_str());
7216
7217 /* may not be busy */
7218 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7219
7220 /* get the path to the executable */
7221 char szPath[RTPATH_MAX];
7222 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7223 size_t sz = strlen(szPath);
7224 szPath[sz++] = RTPATH_DELIMITER;
7225 szPath[sz] = 0;
7226 char *cmd = szPath + sz;
7227 sz = RTPATH_MAX - sz;
7228
7229 int vrc = VINF_SUCCESS;
7230 RTPROCESS pid = NIL_RTPROCESS;
7231
7232 RTENV env = RTENV_DEFAULT;
7233
7234 if (!strEnvironment.isEmpty())
7235 {
7236 char *newEnvStr = NULL;
7237
7238 do
7239 {
7240 /* clone the current environment */
7241 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7242 AssertRCBreakStmt(vrc2, vrc = vrc2);
7243
7244 newEnvStr = RTStrDup(strEnvironment.c_str());
7245 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7246
7247 /* put new variables to the environment
7248 * (ignore empty variable names here since RTEnv API
7249 * intentionally doesn't do that) */
7250 char *var = newEnvStr;
7251 for (char *p = newEnvStr; *p; ++p)
7252 {
7253 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7254 {
7255 *p = '\0';
7256 if (*var)
7257 {
7258 char *val = strchr(var, '=');
7259 if (val)
7260 {
7261 *val++ = '\0';
7262 vrc2 = RTEnvSetEx(env, var, val);
7263 }
7264 else
7265 vrc2 = RTEnvUnsetEx(env, var);
7266 if (RT_FAILURE(vrc2))
7267 break;
7268 }
7269 var = p + 1;
7270 }
7271 }
7272 if (RT_SUCCESS(vrc2) && *var)
7273 vrc2 = RTEnvPutEx(env, var);
7274
7275 AssertRCBreakStmt(vrc2, vrc = vrc2);
7276 }
7277 while (0);
7278
7279 if (newEnvStr != NULL)
7280 RTStrFree(newEnvStr);
7281 }
7282
7283 /* Qt is default */
7284#ifdef VBOX_WITH_QTGUI
7285 if (strType == "gui" || strType == "GUI/Qt")
7286 {
7287# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7288 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7289# else
7290 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7291# endif
7292 Assert(sz >= sizeof(VirtualBox_exe));
7293 strcpy(cmd, VirtualBox_exe);
7294
7295 Utf8Str idStr = mData->mUuid.toString();
7296 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7297 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7298 }
7299#else /* !VBOX_WITH_QTGUI */
7300 if (0)
7301 ;
7302#endif /* VBOX_WITH_QTGUI */
7303
7304 else
7305
7306#ifdef VBOX_WITH_VBOXSDL
7307 if (strType == "sdl" || strType == "GUI/SDL")
7308 {
7309 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7310 Assert(sz >= sizeof(VBoxSDL_exe));
7311 strcpy(cmd, VBoxSDL_exe);
7312
7313 Utf8Str idStr = mData->mUuid.toString();
7314 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7315 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7316 }
7317#else /* !VBOX_WITH_VBOXSDL */
7318 if (0)
7319 ;
7320#endif /* !VBOX_WITH_VBOXSDL */
7321
7322 else
7323
7324#ifdef VBOX_WITH_HEADLESS
7325 if ( strType == "headless"
7326 || strType == "capture"
7327 || strType == "vrdp" /* Deprecated. Same as headless. */
7328 )
7329 {
7330 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7331 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7332 * and a VM works even if the server has not been installed.
7333 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7334 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7335 * differently in 4.0 and 3.x.
7336 */
7337 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7338 Assert(sz >= sizeof(VBoxHeadless_exe));
7339 strcpy(cmd, VBoxHeadless_exe);
7340
7341 Utf8Str idStr = mData->mUuid.toString();
7342 /* Leave space for "--capture" arg. */
7343 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7344 "--startvm", idStr.c_str(),
7345 "--vrde", "config",
7346 0, /* For "--capture". */
7347 0 };
7348 if (strType == "capture")
7349 {
7350 unsigned pos = RT_ELEMENTS(args) - 2;
7351 args[pos] = "--capture";
7352 }
7353 vrc = RTProcCreate(szPath, args, env,
7354#ifdef RT_OS_WINDOWS
7355 RTPROC_FLAGS_NO_WINDOW
7356#else
7357 0
7358#endif
7359 , &pid);
7360 }
7361#else /* !VBOX_WITH_HEADLESS */
7362 if (0)
7363 ;
7364#endif /* !VBOX_WITH_HEADLESS */
7365 else
7366 {
7367 RTEnvDestroy(env);
7368 return setError(E_INVALIDARG,
7369 tr("Invalid session type: '%s'"),
7370 strType.c_str());
7371 }
7372
7373 RTEnvDestroy(env);
7374
7375 if (RT_FAILURE(vrc))
7376 return setError(VBOX_E_IPRT_ERROR,
7377 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7378 mUserData->s.strName.c_str(), vrc);
7379
7380 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7381
7382 /*
7383 * Note that we don't release the lock here before calling the client,
7384 * because it doesn't need to call us back if called with a NULL argument.
7385 * Releasing the lock here is dangerous because we didn't prepare the
7386 * launch data yet, but the client we've just started may happen to be
7387 * too fast and call openSession() that will fail (because of PID, etc.),
7388 * so that the Machine will never get out of the Spawning session state.
7389 */
7390
7391 /* inform the session that it will be a remote one */
7392 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7393 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7394 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7395
7396 if (FAILED(rc))
7397 {
7398 /* restore the session state */
7399 mData->mSession.mState = SessionState_Unlocked;
7400 /* The failure may occur w/o any error info (from RPC), so provide one */
7401 return setError(VBOX_E_VM_ERROR,
7402 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7403 }
7404
7405 /* attach launch data to the machine */
7406 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7407 mData->mSession.mRemoteControls.push_back(aControl);
7408 mData->mSession.mProgress = aProgress;
7409 mData->mSession.mPID = pid;
7410 mData->mSession.mState = SessionState_Spawning;
7411 mData->mSession.mType = strType;
7412
7413 LogFlowThisFuncLeave();
7414 return S_OK;
7415}
7416
7417/**
7418 * Returns @c true if the given machine has an open direct session and returns
7419 * the session machine instance and additional session data (on some platforms)
7420 * if so.
7421 *
7422 * Note that when the method returns @c false, the arguments remain unchanged.
7423 *
7424 * @param aMachine Session machine object.
7425 * @param aControl Direct session control object (optional).
7426 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7427 *
7428 * @note locks this object for reading.
7429 */
7430#if defined(RT_OS_WINDOWS)
7431bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7432 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7433 HANDLE *aIPCSem /*= NULL*/,
7434 bool aAllowClosing /*= false*/)
7435#elif defined(RT_OS_OS2)
7436bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7437 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7438 HMTX *aIPCSem /*= NULL*/,
7439 bool aAllowClosing /*= false*/)
7440#else
7441bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7442 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7443 bool aAllowClosing /*= false*/)
7444#endif
7445{
7446 AutoLimitedCaller autoCaller(this);
7447 AssertComRCReturn(autoCaller.rc(), false);
7448
7449 /* just return false for inaccessible machines */
7450 if (autoCaller.state() != Ready)
7451 return false;
7452
7453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7454
7455 if ( mData->mSession.mState == SessionState_Locked
7456 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7457 )
7458 {
7459 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7460
7461 aMachine = mData->mSession.mMachine;
7462
7463 if (aControl != NULL)
7464 *aControl = mData->mSession.mDirectControl;
7465
7466#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7467 /* Additional session data */
7468 if (aIPCSem != NULL)
7469 *aIPCSem = aMachine->mIPCSem;
7470#endif
7471 return true;
7472 }
7473
7474 return false;
7475}
7476
7477/**
7478 * Returns @c true if the given machine has an spawning direct session and
7479 * returns and additional session data (on some platforms) if so.
7480 *
7481 * Note that when the method returns @c false, the arguments remain unchanged.
7482 *
7483 * @param aPID PID of the spawned direct session process.
7484 *
7485 * @note locks this object for reading.
7486 */
7487#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7488bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7489#else
7490bool Machine::isSessionSpawning()
7491#endif
7492{
7493 AutoLimitedCaller autoCaller(this);
7494 AssertComRCReturn(autoCaller.rc(), false);
7495
7496 /* just return false for inaccessible machines */
7497 if (autoCaller.state() != Ready)
7498 return false;
7499
7500 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7501
7502 if (mData->mSession.mState == SessionState_Spawning)
7503 {
7504#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7505 /* Additional session data */
7506 if (aPID != NULL)
7507 {
7508 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7509 *aPID = mData->mSession.mPID;
7510 }
7511#endif
7512 return true;
7513 }
7514
7515 return false;
7516}
7517
7518/**
7519 * Called from the client watcher thread to check for unexpected client process
7520 * death during Session_Spawning state (e.g. before it successfully opened a
7521 * direct session).
7522 *
7523 * On Win32 and on OS/2, this method is called only when we've got the
7524 * direct client's process termination notification, so it always returns @c
7525 * true.
7526 *
7527 * On other platforms, this method returns @c true if the client process is
7528 * terminated and @c false if it's still alive.
7529 *
7530 * @note Locks this object for writing.
7531 */
7532bool Machine::checkForSpawnFailure()
7533{
7534 AutoCaller autoCaller(this);
7535 if (!autoCaller.isOk())
7536 {
7537 /* nothing to do */
7538 LogFlowThisFunc(("Already uninitialized!\n"));
7539 return true;
7540 }
7541
7542 /* VirtualBox::addProcessToReap() needs a write lock */
7543 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7544
7545 if (mData->mSession.mState != SessionState_Spawning)
7546 {
7547 /* nothing to do */
7548 LogFlowThisFunc(("Not spawning any more!\n"));
7549 return true;
7550 }
7551
7552 HRESULT rc = S_OK;
7553
7554#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7555
7556 /* the process was already unexpectedly terminated, we just need to set an
7557 * error and finalize session spawning */
7558 rc = setError(E_FAIL,
7559 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7560 getName().c_str());
7561#else
7562
7563 /* PID not yet initialized, skip check. */
7564 if (mData->mSession.mPID == NIL_RTPROCESS)
7565 return false;
7566
7567 RTPROCSTATUS status;
7568 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7569 &status);
7570
7571 if (vrc != VERR_PROCESS_RUNNING)
7572 {
7573 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7574 rc = setError(E_FAIL,
7575 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7576 getName().c_str(), status.iStatus);
7577 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7578 rc = setError(E_FAIL,
7579 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7580 getName().c_str(), status.iStatus);
7581 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7582 rc = setError(E_FAIL,
7583 tr("The virtual machine '%s' has terminated abnormally"),
7584 getName().c_str(), status.iStatus);
7585 else
7586 rc = setError(E_FAIL,
7587 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7588 getName().c_str(), rc);
7589 }
7590
7591#endif
7592
7593 if (FAILED(rc))
7594 {
7595 /* Close the remote session, remove the remote control from the list
7596 * and reset session state to Closed (@note keep the code in sync with
7597 * the relevant part in checkForSpawnFailure()). */
7598
7599 Assert(mData->mSession.mRemoteControls.size() == 1);
7600 if (mData->mSession.mRemoteControls.size() == 1)
7601 {
7602 ErrorInfoKeeper eik;
7603 mData->mSession.mRemoteControls.front()->Uninitialize();
7604 }
7605
7606 mData->mSession.mRemoteControls.clear();
7607 mData->mSession.mState = SessionState_Unlocked;
7608
7609 /* finalize the progress after setting the state */
7610 if (!mData->mSession.mProgress.isNull())
7611 {
7612 mData->mSession.mProgress->notifyComplete(rc);
7613 mData->mSession.mProgress.setNull();
7614 }
7615
7616 mParent->addProcessToReap(mData->mSession.mPID);
7617 mData->mSession.mPID = NIL_RTPROCESS;
7618
7619 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7620 return true;
7621 }
7622
7623 return false;
7624}
7625
7626/**
7627 * Checks whether the machine can be registered. If so, commits and saves
7628 * all settings.
7629 *
7630 * @note Must be called from mParent's write lock. Locks this object and
7631 * children for writing.
7632 */
7633HRESULT Machine::prepareRegister()
7634{
7635 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7636
7637 AutoLimitedCaller autoCaller(this);
7638 AssertComRCReturnRC(autoCaller.rc());
7639
7640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7641
7642 /* wait for state dependents to drop to zero */
7643 ensureNoStateDependencies();
7644
7645 if (!mData->mAccessible)
7646 return setError(VBOX_E_INVALID_OBJECT_STATE,
7647 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7648 mUserData->s.strName.c_str(),
7649 mData->mUuid.toString().c_str());
7650
7651 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7652
7653 if (mData->mRegistered)
7654 return setError(VBOX_E_INVALID_OBJECT_STATE,
7655 tr("The machine '%s' with UUID {%s} is already registered"),
7656 mUserData->s.strName.c_str(),
7657 mData->mUuid.toString().c_str());
7658
7659 HRESULT rc = S_OK;
7660
7661 // Ensure the settings are saved. If we are going to be registered and
7662 // no config file exists yet, create it by calling saveSettings() too.
7663 if ( (mData->flModifications)
7664 || (!mData->pMachineConfigFile->fileExists())
7665 )
7666 {
7667 rc = saveSettings(NULL);
7668 // no need to check whether VirtualBox.xml needs saving too since
7669 // we can't have a machine XML file rename pending
7670 if (FAILED(rc)) return rc;
7671 }
7672
7673 /* more config checking goes here */
7674
7675 if (SUCCEEDED(rc))
7676 {
7677 /* we may have had implicit modifications we want to fix on success */
7678 commit();
7679
7680 mData->mRegistered = true;
7681 }
7682 else
7683 {
7684 /* we may have had implicit modifications we want to cancel on failure*/
7685 rollback(false /* aNotify */);
7686 }
7687
7688 return rc;
7689}
7690
7691/**
7692 * Increases the number of objects dependent on the machine state or on the
7693 * registered state. Guarantees that these two states will not change at least
7694 * until #releaseStateDependency() is called.
7695 *
7696 * Depending on the @a aDepType value, additional state checks may be made.
7697 * These checks will set extended error info on failure. See
7698 * #checkStateDependency() for more info.
7699 *
7700 * If this method returns a failure, the dependency is not added and the caller
7701 * is not allowed to rely on any particular machine state or registration state
7702 * value and may return the failed result code to the upper level.
7703 *
7704 * @param aDepType Dependency type to add.
7705 * @param aState Current machine state (NULL if not interested).
7706 * @param aRegistered Current registered state (NULL if not interested).
7707 *
7708 * @note Locks this object for writing.
7709 */
7710HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7711 MachineState_T *aState /* = NULL */,
7712 BOOL *aRegistered /* = NULL */)
7713{
7714 AutoCaller autoCaller(this);
7715 AssertComRCReturnRC(autoCaller.rc());
7716
7717 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7718
7719 HRESULT rc = checkStateDependency(aDepType);
7720 if (FAILED(rc)) return rc;
7721
7722 {
7723 if (mData->mMachineStateChangePending != 0)
7724 {
7725 /* ensureNoStateDependencies() is waiting for state dependencies to
7726 * drop to zero so don't add more. It may make sense to wait a bit
7727 * and retry before reporting an error (since the pending state
7728 * transition should be really quick) but let's just assert for
7729 * now to see if it ever happens on practice. */
7730
7731 AssertFailed();
7732
7733 return setError(E_ACCESSDENIED,
7734 tr("Machine state change is in progress. Please retry the operation later."));
7735 }
7736
7737 ++mData->mMachineStateDeps;
7738 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7739 }
7740
7741 if (aState)
7742 *aState = mData->mMachineState;
7743 if (aRegistered)
7744 *aRegistered = mData->mRegistered;
7745
7746 return S_OK;
7747}
7748
7749/**
7750 * Decreases the number of objects dependent on the machine state.
7751 * Must always complete the #addStateDependency() call after the state
7752 * dependency is no more necessary.
7753 */
7754void Machine::releaseStateDependency()
7755{
7756 AutoCaller autoCaller(this);
7757 AssertComRCReturnVoid(autoCaller.rc());
7758
7759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7760
7761 /* releaseStateDependency() w/o addStateDependency()? */
7762 AssertReturnVoid(mData->mMachineStateDeps != 0);
7763 -- mData->mMachineStateDeps;
7764
7765 if (mData->mMachineStateDeps == 0)
7766 {
7767 /* inform ensureNoStateDependencies() that there are no more deps */
7768 if (mData->mMachineStateChangePending != 0)
7769 {
7770 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7771 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7772 }
7773 }
7774}
7775
7776// protected methods
7777/////////////////////////////////////////////////////////////////////////////
7778
7779/**
7780 * Performs machine state checks based on the @a aDepType value. If a check
7781 * fails, this method will set extended error info, otherwise it will return
7782 * S_OK. It is supposed, that on failure, the caller will immediately return
7783 * the return value of this method to the upper level.
7784 *
7785 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7786 *
7787 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7788 * current state of this machine object allows to change settings of the
7789 * machine (i.e. the machine is not registered, or registered but not running
7790 * and not saved). It is useful to call this method from Machine setters
7791 * before performing any change.
7792 *
7793 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7794 * as for MutableStateDep except that if the machine is saved, S_OK is also
7795 * returned. This is useful in setters which allow changing machine
7796 * properties when it is in the saved state.
7797 *
7798 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7799 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7800 * Aborted).
7801 *
7802 * @param aDepType Dependency type to check.
7803 *
7804 * @note Non Machine based classes should use #addStateDependency() and
7805 * #releaseStateDependency() methods or the smart AutoStateDependency
7806 * template.
7807 *
7808 * @note This method must be called from under this object's read or write
7809 * lock.
7810 */
7811HRESULT Machine::checkStateDependency(StateDependency aDepType)
7812{
7813 switch (aDepType)
7814 {
7815 case AnyStateDep:
7816 {
7817 break;
7818 }
7819 case MutableStateDep:
7820 {
7821 if ( mData->mRegistered
7822 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7823 || ( mData->mMachineState != MachineState_Paused
7824 && mData->mMachineState != MachineState_Running
7825 && mData->mMachineState != MachineState_Aborted
7826 && mData->mMachineState != MachineState_Teleported
7827 && mData->mMachineState != MachineState_PoweredOff
7828 )
7829 )
7830 )
7831 return setError(VBOX_E_INVALID_VM_STATE,
7832 tr("The machine is not mutable (state is %s)"),
7833 Global::stringifyMachineState(mData->mMachineState));
7834 break;
7835 }
7836 case MutableOrSavedStateDep:
7837 {
7838 if ( mData->mRegistered
7839 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7840 || ( mData->mMachineState != MachineState_Paused
7841 && mData->mMachineState != MachineState_Running
7842 && mData->mMachineState != MachineState_Aborted
7843 && mData->mMachineState != MachineState_Teleported
7844 && mData->mMachineState != MachineState_Saved
7845 && mData->mMachineState != MachineState_PoweredOff
7846 )
7847 )
7848 )
7849 return setError(VBOX_E_INVALID_VM_STATE,
7850 tr("The machine is not mutable (state is %s)"),
7851 Global::stringifyMachineState(mData->mMachineState));
7852 break;
7853 }
7854 case OfflineStateDep:
7855 {
7856 if ( mData->mRegistered
7857 && ( !isSessionMachine()
7858 || ( mData->mMachineState != MachineState_PoweredOff
7859 && mData->mMachineState != MachineState_Saved
7860 && mData->mMachineState != MachineState_Aborted
7861 && mData->mMachineState != MachineState_Teleported
7862 )
7863 )
7864 )
7865 return setError(VBOX_E_INVALID_VM_STATE,
7866 tr("The machine is not offline (state is %s)"),
7867 Global::stringifyMachineState(mData->mMachineState));
7868 break;
7869 }
7870 }
7871
7872 return S_OK;
7873}
7874
7875/**
7876 * Helper to initialize all associated child objects and allocate data
7877 * structures.
7878 *
7879 * This method must be called as a part of the object's initialization procedure
7880 * (usually done in the #init() method).
7881 *
7882 * @note Must be called only from #init() or from #registeredInit().
7883 */
7884HRESULT Machine::initDataAndChildObjects()
7885{
7886 AutoCaller autoCaller(this);
7887 AssertComRCReturnRC(autoCaller.rc());
7888 AssertComRCReturn(autoCaller.state() == InInit ||
7889 autoCaller.state() == Limited, E_FAIL);
7890
7891 AssertReturn(!mData->mAccessible, E_FAIL);
7892
7893 /* allocate data structures */
7894 mSSData.allocate();
7895 mUserData.allocate();
7896 mHWData.allocate();
7897 mMediaData.allocate();
7898 mStorageControllers.allocate();
7899
7900 /* initialize mOSTypeId */
7901 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7902
7903 /* create associated BIOS settings object */
7904 unconst(mBIOSSettings).createObject();
7905 mBIOSSettings->init(this);
7906
7907 /* create an associated VRDE object (default is disabled) */
7908 unconst(mVRDEServer).createObject();
7909 mVRDEServer->init(this);
7910
7911 /* create associated serial port objects */
7912 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7913 {
7914 unconst(mSerialPorts[slot]).createObject();
7915 mSerialPorts[slot]->init(this, slot);
7916 }
7917
7918 /* create associated parallel port objects */
7919 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7920 {
7921 unconst(mParallelPorts[slot]).createObject();
7922 mParallelPorts[slot]->init(this, slot);
7923 }
7924
7925 /* create the audio adapter object (always present, default is disabled) */
7926 unconst(mAudioAdapter).createObject();
7927 mAudioAdapter->init(this);
7928
7929 /* create the USB controller object (always present, default is disabled) */
7930 unconst(mUSBController).createObject();
7931 mUSBController->init(this);
7932
7933 /* create associated network adapter objects */
7934 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7935 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7936 {
7937 unconst(mNetworkAdapters[slot]).createObject();
7938 mNetworkAdapters[slot]->init(this, slot);
7939 }
7940
7941 /* create the bandwidth control */
7942 unconst(mBandwidthControl).createObject();
7943 mBandwidthControl->init(this);
7944
7945 return S_OK;
7946}
7947
7948/**
7949 * Helper to uninitialize all associated child objects and to free all data
7950 * structures.
7951 *
7952 * This method must be called as a part of the object's uninitialization
7953 * procedure (usually done in the #uninit() method).
7954 *
7955 * @note Must be called only from #uninit() or from #registeredInit().
7956 */
7957void Machine::uninitDataAndChildObjects()
7958{
7959 AutoCaller autoCaller(this);
7960 AssertComRCReturnVoid(autoCaller.rc());
7961 AssertComRCReturnVoid( autoCaller.state() == InUninit
7962 || autoCaller.state() == Limited);
7963
7964 /* tell all our other child objects we've been uninitialized */
7965 if (mBandwidthControl)
7966 {
7967 mBandwidthControl->uninit();
7968 unconst(mBandwidthControl).setNull();
7969 }
7970
7971 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7972 {
7973 if (mNetworkAdapters[slot])
7974 {
7975 mNetworkAdapters[slot]->uninit();
7976 unconst(mNetworkAdapters[slot]).setNull();
7977 }
7978 }
7979
7980 if (mUSBController)
7981 {
7982 mUSBController->uninit();
7983 unconst(mUSBController).setNull();
7984 }
7985
7986 if (mAudioAdapter)
7987 {
7988 mAudioAdapter->uninit();
7989 unconst(mAudioAdapter).setNull();
7990 }
7991
7992 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7993 {
7994 if (mParallelPorts[slot])
7995 {
7996 mParallelPorts[slot]->uninit();
7997 unconst(mParallelPorts[slot]).setNull();
7998 }
7999 }
8000
8001 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8002 {
8003 if (mSerialPorts[slot])
8004 {
8005 mSerialPorts[slot]->uninit();
8006 unconst(mSerialPorts[slot]).setNull();
8007 }
8008 }
8009
8010 if (mVRDEServer)
8011 {
8012 mVRDEServer->uninit();
8013 unconst(mVRDEServer).setNull();
8014 }
8015
8016 if (mBIOSSettings)
8017 {
8018 mBIOSSettings->uninit();
8019 unconst(mBIOSSettings).setNull();
8020 }
8021
8022 /* Deassociate media (only when a real Machine or a SnapshotMachine
8023 * instance is uninitialized; SessionMachine instances refer to real
8024 * Machine media). This is necessary for a clean re-initialization of
8025 * the VM after successfully re-checking the accessibility state. Note
8026 * that in case of normal Machine or SnapshotMachine uninitialization (as
8027 * a result of unregistering or deleting the snapshot), outdated media
8028 * attachments will already be uninitialized and deleted, so this
8029 * code will not affect them. */
8030 if ( !!mMediaData
8031 && (!isSessionMachine())
8032 )
8033 {
8034 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8035 it != mMediaData->mAttachments.end();
8036 ++it)
8037 {
8038 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8039 if (pMedium.isNull())
8040 continue;
8041 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8042 AssertComRC(rc);
8043 }
8044 }
8045
8046 if (!isSessionMachine() && !isSnapshotMachine())
8047 {
8048 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8049 if (mData->mFirstSnapshot)
8050 {
8051 // snapshots tree is protected by machine write lock; strictly
8052 // this isn't necessary here since we're deleting the entire
8053 // machine, but otherwise we assert in Snapshot::uninit()
8054 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8055 mData->mFirstSnapshot->uninit();
8056 mData->mFirstSnapshot.setNull();
8057 }
8058
8059 mData->mCurrentSnapshot.setNull();
8060 }
8061
8062 /* free data structures (the essential mData structure is not freed here
8063 * since it may be still in use) */
8064 mMediaData.free();
8065 mStorageControllers.free();
8066 mHWData.free();
8067 mUserData.free();
8068 mSSData.free();
8069}
8070
8071/**
8072 * Returns a pointer to the Machine object for this machine that acts like a
8073 * parent for complex machine data objects such as shared folders, etc.
8074 *
8075 * For primary Machine objects and for SnapshotMachine objects, returns this
8076 * object's pointer itself. For SessionMachine objects, returns the peer
8077 * (primary) machine pointer.
8078 */
8079Machine* Machine::getMachine()
8080{
8081 if (isSessionMachine())
8082 return (Machine*)mPeer;
8083 return this;
8084}
8085
8086/**
8087 * Makes sure that there are no machine state dependents. If necessary, waits
8088 * for the number of dependents to drop to zero.
8089 *
8090 * Make sure this method is called from under this object's write lock to
8091 * guarantee that no new dependents may be added when this method returns
8092 * control to the caller.
8093 *
8094 * @note Locks this object for writing. The lock will be released while waiting
8095 * (if necessary).
8096 *
8097 * @warning To be used only in methods that change the machine state!
8098 */
8099void Machine::ensureNoStateDependencies()
8100{
8101 AssertReturnVoid(isWriteLockOnCurrentThread());
8102
8103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8104
8105 /* Wait for all state dependents if necessary */
8106 if (mData->mMachineStateDeps != 0)
8107 {
8108 /* lazy semaphore creation */
8109 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8110 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8111
8112 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8113 mData->mMachineStateDeps));
8114
8115 ++mData->mMachineStateChangePending;
8116
8117 /* reset the semaphore before waiting, the last dependent will signal
8118 * it */
8119 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8120
8121 alock.release();
8122
8123 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8124
8125 alock.acquire();
8126
8127 -- mData->mMachineStateChangePending;
8128 }
8129}
8130
8131/**
8132 * Changes the machine state and informs callbacks.
8133 *
8134 * This method is not intended to fail so it either returns S_OK or asserts (and
8135 * returns a failure).
8136 *
8137 * @note Locks this object for writing.
8138 */
8139HRESULT Machine::setMachineState(MachineState_T aMachineState)
8140{
8141 LogFlowThisFuncEnter();
8142 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8143
8144 AutoCaller autoCaller(this);
8145 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8146
8147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8148
8149 /* wait for state dependents to drop to zero */
8150 ensureNoStateDependencies();
8151
8152 if (mData->mMachineState != aMachineState)
8153 {
8154 mData->mMachineState = aMachineState;
8155
8156 RTTimeNow(&mData->mLastStateChange);
8157
8158 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8159 }
8160
8161 LogFlowThisFuncLeave();
8162 return S_OK;
8163}
8164
8165/**
8166 * Searches for a shared folder with the given logical name
8167 * in the collection of shared folders.
8168 *
8169 * @param aName logical name of the shared folder
8170 * @param aSharedFolder where to return the found object
8171 * @param aSetError whether to set the error info if the folder is
8172 * not found
8173 * @return
8174 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8175 *
8176 * @note
8177 * must be called from under the object's lock!
8178 */
8179HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8180 ComObjPtr<SharedFolder> &aSharedFolder,
8181 bool aSetError /* = false */)
8182{
8183 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8184 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8185 it != mHWData->mSharedFolders.end();
8186 ++it)
8187 {
8188 SharedFolder *pSF = *it;
8189 AutoCaller autoCaller(pSF);
8190 if (pSF->getName() == aName)
8191 {
8192 aSharedFolder = pSF;
8193 rc = S_OK;
8194 break;
8195 }
8196 }
8197
8198 if (aSetError && FAILED(rc))
8199 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8200
8201 return rc;
8202}
8203
8204/**
8205 * Initializes all machine instance data from the given settings structures
8206 * from XML. The exception is the machine UUID which needs special handling
8207 * depending on the caller's use case, so the caller needs to set that herself.
8208 *
8209 * This gets called in several contexts during machine initialization:
8210 *
8211 * -- When machine XML exists on disk already and needs to be loaded into memory,
8212 * for example, from registeredInit() to load all registered machines on
8213 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8214 * attached to the machine should be part of some media registry already.
8215 *
8216 * -- During OVF import, when a machine config has been constructed from an
8217 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8218 * ensure that the media listed as attachments in the config (which have
8219 * been imported from the OVF) receive the correct registry ID.
8220 *
8221 * -- During VM cloning.
8222 *
8223 * @param config Machine settings from XML.
8224 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8225 * @return
8226 */
8227HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8228 const Guid *puuidRegistry)
8229{
8230 // copy name, description, OS type, teleporter, UTC etc.
8231 mUserData->s = config.machineUserData;
8232
8233 // look up the object by Id to check it is valid
8234 ComPtr<IGuestOSType> guestOSType;
8235 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8236 guestOSType.asOutParam());
8237 if (FAILED(rc)) return rc;
8238
8239 // stateFile (optional)
8240 if (config.strStateFile.isEmpty())
8241 mSSData->strStateFilePath.setNull();
8242 else
8243 {
8244 Utf8Str stateFilePathFull(config.strStateFile);
8245 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8246 if (RT_FAILURE(vrc))
8247 return setError(E_FAIL,
8248 tr("Invalid saved state file path '%s' (%Rrc)"),
8249 config.strStateFile.c_str(),
8250 vrc);
8251 mSSData->strStateFilePath = stateFilePathFull;
8252 }
8253
8254 // snapshot folder needs special processing so set it again
8255 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8256 if (FAILED(rc)) return rc;
8257
8258 /* Copy the extra data items (Not in any case config is already the same as
8259 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8260 * make sure the extra data map is copied). */
8261 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8262
8263 /* currentStateModified (optional, default is true) */
8264 mData->mCurrentStateModified = config.fCurrentStateModified;
8265
8266 mData->mLastStateChange = config.timeLastStateChange;
8267
8268 /*
8269 * note: all mUserData members must be assigned prior this point because
8270 * we need to commit changes in order to let mUserData be shared by all
8271 * snapshot machine instances.
8272 */
8273 mUserData.commitCopy();
8274
8275 // machine registry, if present (must be loaded before snapshots)
8276 if (config.canHaveOwnMediaRegistry())
8277 {
8278 // determine machine folder
8279 Utf8Str strMachineFolder = getSettingsFileFull();
8280 strMachineFolder.stripFilename();
8281 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8282 config.mediaRegistry,
8283 strMachineFolder);
8284 if (FAILED(rc)) return rc;
8285 }
8286
8287 /* Snapshot node (optional) */
8288 size_t cRootSnapshots;
8289 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8290 {
8291 // there must be only one root snapshot
8292 Assert(cRootSnapshots == 1);
8293
8294 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8295
8296 rc = loadSnapshot(snap,
8297 config.uuidCurrentSnapshot,
8298 NULL); // no parent == first snapshot
8299 if (FAILED(rc)) return rc;
8300 }
8301
8302 // hardware data
8303 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8304 if (FAILED(rc)) return rc;
8305
8306 // load storage controllers
8307 rc = loadStorageControllers(config.storageMachine,
8308 puuidRegistry,
8309 NULL /* puuidSnapshot */);
8310 if (FAILED(rc)) return rc;
8311
8312 /*
8313 * NOTE: the assignment below must be the last thing to do,
8314 * otherwise it will be not possible to change the settings
8315 * somewhere in the code above because all setters will be
8316 * blocked by checkStateDependency(MutableStateDep).
8317 */
8318
8319 /* set the machine state to Aborted or Saved when appropriate */
8320 if (config.fAborted)
8321 {
8322 mSSData->strStateFilePath.setNull();
8323
8324 /* no need to use setMachineState() during init() */
8325 mData->mMachineState = MachineState_Aborted;
8326 }
8327 else if (!mSSData->strStateFilePath.isEmpty())
8328 {
8329 /* no need to use setMachineState() during init() */
8330 mData->mMachineState = MachineState_Saved;
8331 }
8332
8333 // after loading settings, we are no longer different from the XML on disk
8334 mData->flModifications = 0;
8335
8336 return S_OK;
8337}
8338
8339/**
8340 * Recursively loads all snapshots starting from the given.
8341 *
8342 * @param aNode <Snapshot> node.
8343 * @param aCurSnapshotId Current snapshot ID from the settings file.
8344 * @param aParentSnapshot Parent snapshot.
8345 */
8346HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8347 const Guid &aCurSnapshotId,
8348 Snapshot *aParentSnapshot)
8349{
8350 AssertReturn(!isSnapshotMachine(), E_FAIL);
8351 AssertReturn(!isSessionMachine(), E_FAIL);
8352
8353 HRESULT rc = S_OK;
8354
8355 Utf8Str strStateFile;
8356 if (!data.strStateFile.isEmpty())
8357 {
8358 /* optional */
8359 strStateFile = data.strStateFile;
8360 int vrc = calculateFullPath(strStateFile, strStateFile);
8361 if (RT_FAILURE(vrc))
8362 return setError(E_FAIL,
8363 tr("Invalid saved state file path '%s' (%Rrc)"),
8364 strStateFile.c_str(),
8365 vrc);
8366 }
8367
8368 /* create a snapshot machine object */
8369 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8370 pSnapshotMachine.createObject();
8371 rc = pSnapshotMachine->initFromSettings(this,
8372 data.hardware,
8373 &data.debugging,
8374 &data.autostart,
8375 data.storage,
8376 data.uuid.ref(),
8377 strStateFile);
8378 if (FAILED(rc)) return rc;
8379
8380 /* create a snapshot object */
8381 ComObjPtr<Snapshot> pSnapshot;
8382 pSnapshot.createObject();
8383 /* initialize the snapshot */
8384 rc = pSnapshot->init(mParent, // VirtualBox object
8385 data.uuid,
8386 data.strName,
8387 data.strDescription,
8388 data.timestamp,
8389 pSnapshotMachine,
8390 aParentSnapshot);
8391 if (FAILED(rc)) return rc;
8392
8393 /* memorize the first snapshot if necessary */
8394 if (!mData->mFirstSnapshot)
8395 mData->mFirstSnapshot = pSnapshot;
8396
8397 /* memorize the current snapshot when appropriate */
8398 if ( !mData->mCurrentSnapshot
8399 && pSnapshot->getId() == aCurSnapshotId
8400 )
8401 mData->mCurrentSnapshot = pSnapshot;
8402
8403 // now create the children
8404 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8405 it != data.llChildSnapshots.end();
8406 ++it)
8407 {
8408 const settings::Snapshot &childData = *it;
8409 // recurse
8410 rc = loadSnapshot(childData,
8411 aCurSnapshotId,
8412 pSnapshot); // parent = the one we created above
8413 if (FAILED(rc)) return rc;
8414 }
8415
8416 return rc;
8417}
8418
8419/**
8420 * Loads settings into mHWData.
8421 *
8422 * @param data Reference to the hardware settings.
8423 * @param pDbg Pointer to the debugging settings.
8424 * @param pAutostart Pointer to the autostart settings.
8425 */
8426HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8427 const settings::Autostart *pAutostart)
8428{
8429 AssertReturn(!isSessionMachine(), E_FAIL);
8430
8431 HRESULT rc = S_OK;
8432
8433 try
8434 {
8435 /* The hardware version attribute (optional). */
8436 mHWData->mHWVersion = data.strVersion;
8437 mHWData->mHardwareUUID = data.uuid;
8438
8439 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8440 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8441 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8442 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8443 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8444 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8445 mHWData->mPAEEnabled = data.fPAE;
8446 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8447
8448 mHWData->mCPUCount = data.cCPUs;
8449 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8450 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8451
8452 // cpu
8453 if (mHWData->mCPUHotPlugEnabled)
8454 {
8455 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8456 it != data.llCpus.end();
8457 ++it)
8458 {
8459 const settings::Cpu &cpu = *it;
8460
8461 mHWData->mCPUAttached[cpu.ulId] = true;
8462 }
8463 }
8464
8465 // cpuid leafs
8466 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8467 it != data.llCpuIdLeafs.end();
8468 ++it)
8469 {
8470 const settings::CpuIdLeaf &leaf = *it;
8471
8472 switch (leaf.ulId)
8473 {
8474 case 0x0:
8475 case 0x1:
8476 case 0x2:
8477 case 0x3:
8478 case 0x4:
8479 case 0x5:
8480 case 0x6:
8481 case 0x7:
8482 case 0x8:
8483 case 0x9:
8484 case 0xA:
8485 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8486 break;
8487
8488 case 0x80000000:
8489 case 0x80000001:
8490 case 0x80000002:
8491 case 0x80000003:
8492 case 0x80000004:
8493 case 0x80000005:
8494 case 0x80000006:
8495 case 0x80000007:
8496 case 0x80000008:
8497 case 0x80000009:
8498 case 0x8000000A:
8499 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8500 break;
8501
8502 default:
8503 /* just ignore */
8504 break;
8505 }
8506 }
8507
8508 mHWData->mMemorySize = data.ulMemorySizeMB;
8509 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8510
8511 // boot order
8512 for (size_t i = 0;
8513 i < RT_ELEMENTS(mHWData->mBootOrder);
8514 i++)
8515 {
8516 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8517 if (it == data.mapBootOrder.end())
8518 mHWData->mBootOrder[i] = DeviceType_Null;
8519 else
8520 mHWData->mBootOrder[i] = it->second;
8521 }
8522
8523 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8524 mHWData->mMonitorCount = data.cMonitors;
8525 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8526 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8527 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8528 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8529 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8530 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8531 mHWData->mFirmwareType = data.firmwareType;
8532 mHWData->mPointingHIDType = data.pointingHIDType;
8533 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8534 mHWData->mChipsetType = data.chipsetType;
8535 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8536 mHWData->mHPETEnabled = data.fHPETEnabled;
8537
8538 /* VRDEServer */
8539 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8540 if (FAILED(rc)) return rc;
8541
8542 /* BIOS */
8543 rc = mBIOSSettings->loadSettings(data.biosSettings);
8544 if (FAILED(rc)) return rc;
8545
8546 // Bandwidth control (must come before network adapters)
8547 rc = mBandwidthControl->loadSettings(data.ioSettings);
8548 if (FAILED(rc)) return rc;
8549
8550 /* USB Controller */
8551 rc = mUSBController->loadSettings(data.usbController);
8552 if (FAILED(rc)) return rc;
8553
8554 // network adapters
8555 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8556 uint32_t oldCount = mNetworkAdapters.size();
8557 if (newCount > oldCount)
8558 {
8559 mNetworkAdapters.resize(newCount);
8560 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8561 {
8562 unconst(mNetworkAdapters[slot]).createObject();
8563 mNetworkAdapters[slot]->init(this, slot);
8564 }
8565 }
8566 else if (newCount < oldCount)
8567 mNetworkAdapters.resize(newCount);
8568 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8569 it != data.llNetworkAdapters.end();
8570 ++it)
8571 {
8572 const settings::NetworkAdapter &nic = *it;
8573
8574 /* slot unicity is guaranteed by XML Schema */
8575 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8576 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8577 if (FAILED(rc)) return rc;
8578 }
8579
8580 // serial ports
8581 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8582 it != data.llSerialPorts.end();
8583 ++it)
8584 {
8585 const settings::SerialPort &s = *it;
8586
8587 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8588 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8589 if (FAILED(rc)) return rc;
8590 }
8591
8592 // parallel ports (optional)
8593 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8594 it != data.llParallelPorts.end();
8595 ++it)
8596 {
8597 const settings::ParallelPort &p = *it;
8598
8599 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8600 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8601 if (FAILED(rc)) return rc;
8602 }
8603
8604 /* AudioAdapter */
8605 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8606 if (FAILED(rc)) return rc;
8607
8608 /* Shared folders */
8609 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8610 it != data.llSharedFolders.end();
8611 ++it)
8612 {
8613 const settings::SharedFolder &sf = *it;
8614
8615 ComObjPtr<SharedFolder> sharedFolder;
8616 /* Check for double entries. Not allowed! */
8617 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8618 if (SUCCEEDED(rc))
8619 return setError(VBOX_E_OBJECT_IN_USE,
8620 tr("Shared folder named '%s' already exists"),
8621 sf.strName.c_str());
8622
8623 /* Create the new shared folder. Don't break on error. This will be
8624 * reported when the machine starts. */
8625 sharedFolder.createObject();
8626 rc = sharedFolder->init(getMachine(),
8627 sf.strName,
8628 sf.strHostPath,
8629 RT_BOOL(sf.fWritable),
8630 RT_BOOL(sf.fAutoMount),
8631 false /* fFailOnError */);
8632 if (FAILED(rc)) return rc;
8633 mHWData->mSharedFolders.push_back(sharedFolder);
8634 }
8635
8636 // Clipboard
8637 mHWData->mClipboardMode = data.clipboardMode;
8638
8639 // drag'n'drop
8640 mHWData->mDragAndDropMode = data.dragAndDropMode;
8641
8642 // guest settings
8643 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8644
8645 // IO settings
8646 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8647 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8648
8649 // Host PCI devices
8650 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8651 it != data.pciAttachments.end();
8652 ++it)
8653 {
8654 const settings::HostPCIDeviceAttachment &hpda = *it;
8655 ComObjPtr<PCIDeviceAttachment> pda;
8656
8657 pda.createObject();
8658 pda->loadSettings(this, hpda);
8659 mHWData->mPCIDeviceAssignments.push_back(pda);
8660 }
8661
8662 /*
8663 * (The following isn't really real hardware, but it lives in HWData
8664 * for reasons of convenience.)
8665 */
8666
8667#ifdef VBOX_WITH_GUEST_PROPS
8668 /* Guest properties (optional) */
8669 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8670 it != data.llGuestProperties.end();
8671 ++it)
8672 {
8673 const settings::GuestProperty &prop = *it;
8674 uint32_t fFlags = guestProp::NILFLAG;
8675 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8676 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8677 mHWData->mGuestProperties.push_back(property);
8678 }
8679
8680 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8681#endif /* VBOX_WITH_GUEST_PROPS defined */
8682
8683 rc = loadDebugging(pDbg);
8684 if (FAILED(rc))
8685 return rc;
8686
8687 mHWData->mAutostart = *pAutostart;
8688 }
8689 catch(std::bad_alloc &)
8690 {
8691 return E_OUTOFMEMORY;
8692 }
8693
8694 AssertComRC(rc);
8695 return rc;
8696}
8697
8698/**
8699 * Called from Machine::loadHardware() to load the debugging settings of the
8700 * machine.
8701 *
8702 * @param pDbg Pointer to the settings.
8703 */
8704HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8705{
8706 mHWData->mDebugging = *pDbg;
8707 /* no more processing currently required, this will probably change. */
8708 return S_OK;
8709}
8710
8711/**
8712 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8713 *
8714 * @param data
8715 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8716 * @param puuidSnapshot
8717 * @return
8718 */
8719HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8720 const Guid *puuidRegistry,
8721 const Guid *puuidSnapshot)
8722{
8723 AssertReturn(!isSessionMachine(), E_FAIL);
8724
8725 HRESULT rc = S_OK;
8726
8727 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8728 it != data.llStorageControllers.end();
8729 ++it)
8730 {
8731 const settings::StorageController &ctlData = *it;
8732
8733 ComObjPtr<StorageController> pCtl;
8734 /* Try to find one with the name first. */
8735 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8736 if (SUCCEEDED(rc))
8737 return setError(VBOX_E_OBJECT_IN_USE,
8738 tr("Storage controller named '%s' already exists"),
8739 ctlData.strName.c_str());
8740
8741 pCtl.createObject();
8742 rc = pCtl->init(this,
8743 ctlData.strName,
8744 ctlData.storageBus,
8745 ctlData.ulInstance,
8746 ctlData.fBootable);
8747 if (FAILED(rc)) return rc;
8748
8749 mStorageControllers->push_back(pCtl);
8750
8751 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8752 if (FAILED(rc)) return rc;
8753
8754 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8755 if (FAILED(rc)) return rc;
8756
8757 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8758 if (FAILED(rc)) return rc;
8759
8760 /* Set IDE emulation settings (only for AHCI controller). */
8761 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8762 {
8763 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8764 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8765 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8766 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8767 )
8768 return rc;
8769 }
8770
8771 /* Load the attached devices now. */
8772 rc = loadStorageDevices(pCtl,
8773 ctlData,
8774 puuidRegistry,
8775 puuidSnapshot);
8776 if (FAILED(rc)) return rc;
8777 }
8778
8779 return S_OK;
8780}
8781
8782/**
8783 * Called from loadStorageControllers for a controller's devices.
8784 *
8785 * @param aStorageController
8786 * @param data
8787 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8788 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8789 * @return
8790 */
8791HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8792 const settings::StorageController &data,
8793 const Guid *puuidRegistry,
8794 const Guid *puuidSnapshot)
8795{
8796 HRESULT rc = S_OK;
8797
8798 /* paranoia: detect duplicate attachments */
8799 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8800 it != data.llAttachedDevices.end();
8801 ++it)
8802 {
8803 const settings::AttachedDevice &ad = *it;
8804
8805 for (settings::AttachedDevicesList::const_iterator it2 = it;
8806 it2 != data.llAttachedDevices.end();
8807 ++it2)
8808 {
8809 if (it == it2)
8810 continue;
8811
8812 const settings::AttachedDevice &ad2 = *it2;
8813
8814 if ( ad.lPort == ad2.lPort
8815 && ad.lDevice == ad2.lDevice)
8816 {
8817 return setError(E_FAIL,
8818 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8819 aStorageController->getName().c_str(),
8820 ad.lPort,
8821 ad.lDevice,
8822 mUserData->s.strName.c_str());
8823 }
8824 }
8825 }
8826
8827 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8828 it != data.llAttachedDevices.end();
8829 ++it)
8830 {
8831 const settings::AttachedDevice &dev = *it;
8832 ComObjPtr<Medium> medium;
8833
8834 switch (dev.deviceType)
8835 {
8836 case DeviceType_Floppy:
8837 case DeviceType_DVD:
8838 if (dev.strHostDriveSrc.isNotEmpty())
8839 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8840 else
8841 rc = mParent->findRemoveableMedium(dev.deviceType,
8842 dev.uuid,
8843 false /* fRefresh */,
8844 false /* aSetError */,
8845 medium);
8846 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8847 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8848 rc = S_OK;
8849 break;
8850
8851 case DeviceType_HardDisk:
8852 {
8853 /* find a hard disk by UUID */
8854 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8855 if (FAILED(rc))
8856 {
8857 if (isSnapshotMachine())
8858 {
8859 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8860 // so the user knows that the bad disk is in a snapshot somewhere
8861 com::ErrorInfo info;
8862 return setError(E_FAIL,
8863 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8864 puuidSnapshot->raw(),
8865 info.getText().raw());
8866 }
8867 else
8868 return rc;
8869 }
8870
8871 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8872
8873 if (medium->getType() == MediumType_Immutable)
8874 {
8875 if (isSnapshotMachine())
8876 return setError(E_FAIL,
8877 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8878 "of the virtual machine '%s' ('%s')"),
8879 medium->getLocationFull().c_str(),
8880 dev.uuid.raw(),
8881 puuidSnapshot->raw(),
8882 mUserData->s.strName.c_str(),
8883 mData->m_strConfigFileFull.c_str());
8884
8885 return setError(E_FAIL,
8886 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8887 medium->getLocationFull().c_str(),
8888 dev.uuid.raw(),
8889 mUserData->s.strName.c_str(),
8890 mData->m_strConfigFileFull.c_str());
8891 }
8892
8893 if (medium->getType() == MediumType_MultiAttach)
8894 {
8895 if (isSnapshotMachine())
8896 return setError(E_FAIL,
8897 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8898 "of the virtual machine '%s' ('%s')"),
8899 medium->getLocationFull().c_str(),
8900 dev.uuid.raw(),
8901 puuidSnapshot->raw(),
8902 mUserData->s.strName.c_str(),
8903 mData->m_strConfigFileFull.c_str());
8904
8905 return setError(E_FAIL,
8906 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8907 medium->getLocationFull().c_str(),
8908 dev.uuid.raw(),
8909 mUserData->s.strName.c_str(),
8910 mData->m_strConfigFileFull.c_str());
8911 }
8912
8913 if ( !isSnapshotMachine()
8914 && medium->getChildren().size() != 0
8915 )
8916 return setError(E_FAIL,
8917 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8918 "because it has %d differencing child hard disks"),
8919 medium->getLocationFull().c_str(),
8920 dev.uuid.raw(),
8921 mUserData->s.strName.c_str(),
8922 mData->m_strConfigFileFull.c_str(),
8923 medium->getChildren().size());
8924
8925 if (findAttachment(mMediaData->mAttachments,
8926 medium))
8927 return setError(E_FAIL,
8928 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8929 medium->getLocationFull().c_str(),
8930 dev.uuid.raw(),
8931 mUserData->s.strName.c_str(),
8932 mData->m_strConfigFileFull.c_str());
8933
8934 break;
8935 }
8936
8937 default:
8938 return setError(E_FAIL,
8939 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8940 medium->getLocationFull().c_str(),
8941 mUserData->s.strName.c_str(),
8942 mData->m_strConfigFileFull.c_str());
8943 }
8944
8945 if (FAILED(rc))
8946 break;
8947
8948 /* Bandwidth groups are loaded at this point. */
8949 ComObjPtr<BandwidthGroup> pBwGroup;
8950
8951 if (!dev.strBwGroup.isEmpty())
8952 {
8953 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8954 if (FAILED(rc))
8955 return setError(E_FAIL,
8956 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8957 medium->getLocationFull().c_str(),
8958 dev.strBwGroup.c_str(),
8959 mUserData->s.strName.c_str(),
8960 mData->m_strConfigFileFull.c_str());
8961 pBwGroup->reference();
8962 }
8963
8964 const Bstr controllerName = aStorageController->getName();
8965 ComObjPtr<MediumAttachment> pAttachment;
8966 pAttachment.createObject();
8967 rc = pAttachment->init(this,
8968 medium,
8969 controllerName,
8970 dev.lPort,
8971 dev.lDevice,
8972 dev.deviceType,
8973 false,
8974 dev.fPassThrough,
8975 dev.fTempEject,
8976 dev.fNonRotational,
8977 dev.fDiscard,
8978 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8979 if (FAILED(rc)) break;
8980
8981 /* associate the medium with this machine and snapshot */
8982 if (!medium.isNull())
8983 {
8984 AutoCaller medCaller(medium);
8985 if (FAILED(medCaller.rc())) return medCaller.rc();
8986 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8987
8988 if (isSnapshotMachine())
8989 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8990 else
8991 rc = medium->addBackReference(mData->mUuid);
8992 /* If the medium->addBackReference fails it sets an appropriate
8993 * error message, so no need to do any guesswork here. */
8994
8995 if (puuidRegistry)
8996 // caller wants registry ID to be set on all attached media (OVF import case)
8997 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8998 }
8999
9000 if (FAILED(rc))
9001 break;
9002
9003 /* back up mMediaData to let registeredInit() properly rollback on failure
9004 * (= limited accessibility) */
9005 setModified(IsModified_Storage);
9006 mMediaData.backup();
9007 mMediaData->mAttachments.push_back(pAttachment);
9008 }
9009
9010 return rc;
9011}
9012
9013/**
9014 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9015 *
9016 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9017 * @param aSnapshot where to return the found snapshot
9018 * @param aSetError true to set extended error info on failure
9019 */
9020HRESULT Machine::findSnapshotById(const Guid &aId,
9021 ComObjPtr<Snapshot> &aSnapshot,
9022 bool aSetError /* = false */)
9023{
9024 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9025
9026 if (!mData->mFirstSnapshot)
9027 {
9028 if (aSetError)
9029 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9030 return E_FAIL;
9031 }
9032
9033 if (aId.isZero())
9034 aSnapshot = mData->mFirstSnapshot;
9035 else
9036 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9037
9038 if (!aSnapshot)
9039 {
9040 if (aSetError)
9041 return setError(E_FAIL,
9042 tr("Could not find a snapshot with UUID {%s}"),
9043 aId.toString().c_str());
9044 return E_FAIL;
9045 }
9046
9047 return S_OK;
9048}
9049
9050/**
9051 * Returns the snapshot with the given name or fails of no such snapshot.
9052 *
9053 * @param aName snapshot name to find
9054 * @param aSnapshot where to return the found snapshot
9055 * @param aSetError true to set extended error info on failure
9056 */
9057HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9058 ComObjPtr<Snapshot> &aSnapshot,
9059 bool aSetError /* = false */)
9060{
9061 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9062
9063 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9064
9065 if (!mData->mFirstSnapshot)
9066 {
9067 if (aSetError)
9068 return setError(VBOX_E_OBJECT_NOT_FOUND,
9069 tr("This machine does not have any snapshots"));
9070 return VBOX_E_OBJECT_NOT_FOUND;
9071 }
9072
9073 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9074
9075 if (!aSnapshot)
9076 {
9077 if (aSetError)
9078 return setError(VBOX_E_OBJECT_NOT_FOUND,
9079 tr("Could not find a snapshot named '%s'"), strName.c_str());
9080 return VBOX_E_OBJECT_NOT_FOUND;
9081 }
9082
9083 return S_OK;
9084}
9085
9086/**
9087 * Returns a storage controller object with the given name.
9088 *
9089 * @param aName storage controller name to find
9090 * @param aStorageController where to return the found storage controller
9091 * @param aSetError true to set extended error info on failure
9092 */
9093HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9094 ComObjPtr<StorageController> &aStorageController,
9095 bool aSetError /* = false */)
9096{
9097 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9098
9099 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9100 it != mStorageControllers->end();
9101 ++it)
9102 {
9103 if ((*it)->getName() == aName)
9104 {
9105 aStorageController = (*it);
9106 return S_OK;
9107 }
9108 }
9109
9110 if (aSetError)
9111 return setError(VBOX_E_OBJECT_NOT_FOUND,
9112 tr("Could not find a storage controller named '%s'"),
9113 aName.c_str());
9114 return VBOX_E_OBJECT_NOT_FOUND;
9115}
9116
9117HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9118 MediaData::AttachmentList &atts)
9119{
9120 AutoCaller autoCaller(this);
9121 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9122
9123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9124
9125 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9126 it != mMediaData->mAttachments.end();
9127 ++it)
9128 {
9129 const ComObjPtr<MediumAttachment> &pAtt = *it;
9130
9131 // should never happen, but deal with NULL pointers in the list.
9132 AssertStmt(!pAtt.isNull(), continue);
9133
9134 // getControllerName() needs caller+read lock
9135 AutoCaller autoAttCaller(pAtt);
9136 if (FAILED(autoAttCaller.rc()))
9137 {
9138 atts.clear();
9139 return autoAttCaller.rc();
9140 }
9141 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9142
9143 if (pAtt->getControllerName() == aName)
9144 atts.push_back(pAtt);
9145 }
9146
9147 return S_OK;
9148}
9149
9150/**
9151 * Helper for #saveSettings. Cares about renaming the settings directory and
9152 * file if the machine name was changed and about creating a new settings file
9153 * if this is a new machine.
9154 *
9155 * @note Must be never called directly but only from #saveSettings().
9156 */
9157HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9158{
9159 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9160
9161 HRESULT rc = S_OK;
9162
9163 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9164
9165 /// @todo need to handle primary group change, too
9166
9167 /* attempt to rename the settings file if machine name is changed */
9168 if ( mUserData->s.fNameSync
9169 && mUserData.isBackedUp()
9170 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9171 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9172 )
9173 {
9174 bool dirRenamed = false;
9175 bool fileRenamed = false;
9176
9177 Utf8Str configFile, newConfigFile;
9178 Utf8Str configFilePrev, newConfigFilePrev;
9179 Utf8Str configDir, newConfigDir;
9180
9181 do
9182 {
9183 int vrc = VINF_SUCCESS;
9184
9185 Utf8Str name = mUserData.backedUpData()->s.strName;
9186 Utf8Str newName = mUserData->s.strName;
9187 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9188 if (group == "/")
9189 group.setNull();
9190 Utf8Str newGroup = mUserData->s.llGroups.front();
9191 if (newGroup == "/")
9192 newGroup.setNull();
9193
9194 configFile = mData->m_strConfigFileFull;
9195
9196 /* first, rename the directory if it matches the group and machine name */
9197 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9198 group.c_str(), RTPATH_DELIMITER, name.c_str());
9199 /** @todo hack, make somehow use of ComposeMachineFilename */
9200 if (mUserData->s.fDirectoryIncludesUUID)
9201 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9202 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9203 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9204 /** @todo hack, make somehow use of ComposeMachineFilename */
9205 if (mUserData->s.fDirectoryIncludesUUID)
9206 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9207 configDir = configFile;
9208 configDir.stripFilename();
9209 newConfigDir = configDir;
9210 if ( configDir.length() >= groupPlusName.length()
9211 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9212 {
9213 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9214 Utf8Str newConfigBaseDir(newConfigDir);
9215 newConfigDir.append(newGroupPlusName);
9216 /* consistency: use \ if appropriate on the platform */
9217 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9218 /* new dir and old dir cannot be equal here because of 'if'
9219 * above and because name != newName */
9220 Assert(configDir != newConfigDir);
9221 if (!fSettingsFileIsNew)
9222 {
9223 /* perform real rename only if the machine is not new */
9224 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9225 if ( vrc == VERR_FILE_NOT_FOUND
9226 || vrc == VERR_PATH_NOT_FOUND)
9227 {
9228 /* create the parent directory, then retry renaming */
9229 Utf8Str parent(newConfigDir);
9230 parent.stripFilename();
9231 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9232 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9233 }
9234 if (RT_FAILURE(vrc))
9235 {
9236 rc = setError(E_FAIL,
9237 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9238 configDir.c_str(),
9239 newConfigDir.c_str(),
9240 vrc);
9241 break;
9242 }
9243 /* delete subdirectories which are no longer needed */
9244 Utf8Str dir(configDir);
9245 dir.stripFilename();
9246 while (dir != newConfigBaseDir && dir != ".")
9247 {
9248 vrc = RTDirRemove(dir.c_str());
9249 if (RT_FAILURE(vrc))
9250 break;
9251 dir.stripFilename();
9252 }
9253 dirRenamed = true;
9254 }
9255 }
9256
9257 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9258 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9259
9260 /* then try to rename the settings file itself */
9261 if (newConfigFile != configFile)
9262 {
9263 /* get the path to old settings file in renamed directory */
9264 configFile = Utf8StrFmt("%s%c%s",
9265 newConfigDir.c_str(),
9266 RTPATH_DELIMITER,
9267 RTPathFilename(configFile.c_str()));
9268 if (!fSettingsFileIsNew)
9269 {
9270 /* perform real rename only if the machine is not new */
9271 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9272 if (RT_FAILURE(vrc))
9273 {
9274 rc = setError(E_FAIL,
9275 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9276 configFile.c_str(),
9277 newConfigFile.c_str(),
9278 vrc);
9279 break;
9280 }
9281 fileRenamed = true;
9282 configFilePrev = configFile;
9283 configFilePrev += "-prev";
9284 newConfigFilePrev = newConfigFile;
9285 newConfigFilePrev += "-prev";
9286 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9287 }
9288 }
9289
9290 // update m_strConfigFileFull amd mConfigFile
9291 mData->m_strConfigFileFull = newConfigFile;
9292 // compute the relative path too
9293 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9294
9295 // store the old and new so that VirtualBox::saveSettings() can update
9296 // the media registry
9297 if ( mData->mRegistered
9298 && configDir != newConfigDir)
9299 {
9300 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9301
9302 if (pfNeedsGlobalSaveSettings)
9303 *pfNeedsGlobalSaveSettings = true;
9304 }
9305
9306 // in the saved state file path, replace the old directory with the new directory
9307 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9308 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9309
9310 // and do the same thing for the saved state file paths of all the online snapshots
9311 if (mData->mFirstSnapshot)
9312 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9313 newConfigDir.c_str());
9314 }
9315 while (0);
9316
9317 if (FAILED(rc))
9318 {
9319 /* silently try to rename everything back */
9320 if (fileRenamed)
9321 {
9322 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9323 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9324 }
9325 if (dirRenamed)
9326 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9327 }
9328
9329 if (FAILED(rc)) return rc;
9330 }
9331
9332 if (fSettingsFileIsNew)
9333 {
9334 /* create a virgin config file */
9335 int vrc = VINF_SUCCESS;
9336
9337 /* ensure the settings directory exists */
9338 Utf8Str path(mData->m_strConfigFileFull);
9339 path.stripFilename();
9340 if (!RTDirExists(path.c_str()))
9341 {
9342 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9343 if (RT_FAILURE(vrc))
9344 {
9345 return setError(E_FAIL,
9346 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9347 path.c_str(),
9348 vrc);
9349 }
9350 }
9351
9352 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9353 path = Utf8Str(mData->m_strConfigFileFull);
9354 RTFILE f = NIL_RTFILE;
9355 vrc = RTFileOpen(&f, path.c_str(),
9356 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9357 if (RT_FAILURE(vrc))
9358 return setError(E_FAIL,
9359 tr("Could not create the settings file '%s' (%Rrc)"),
9360 path.c_str(),
9361 vrc);
9362 RTFileClose(f);
9363 }
9364
9365 return rc;
9366}
9367
9368/**
9369 * Saves and commits machine data, user data and hardware data.
9370 *
9371 * Note that on failure, the data remains uncommitted.
9372 *
9373 * @a aFlags may combine the following flags:
9374 *
9375 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9376 * Used when saving settings after an operation that makes them 100%
9377 * correspond to the settings from the current snapshot.
9378 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9379 * #isReallyModified() returns false. This is necessary for cases when we
9380 * change machine data directly, not through the backup()/commit() mechanism.
9381 * - SaveS_Force: settings will be saved without doing a deep compare of the
9382 * settings structures. This is used when this is called because snapshots
9383 * have changed to avoid the overhead of the deep compare.
9384 *
9385 * @note Must be called from under this object's write lock. Locks children for
9386 * writing.
9387 *
9388 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9389 * initialized to false and that will be set to true by this function if
9390 * the caller must invoke VirtualBox::saveSettings() because the global
9391 * settings have changed. This will happen if a machine rename has been
9392 * saved and the global machine and media registries will therefore need
9393 * updating.
9394 */
9395HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9396 int aFlags /*= 0*/)
9397{
9398 LogFlowThisFuncEnter();
9399
9400 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9401
9402 /* make sure child objects are unable to modify the settings while we are
9403 * saving them */
9404 ensureNoStateDependencies();
9405
9406 AssertReturn(!isSnapshotMachine(),
9407 E_FAIL);
9408
9409 HRESULT rc = S_OK;
9410 bool fNeedsWrite = false;
9411
9412 /* First, prepare to save settings. It will care about renaming the
9413 * settings directory and file if the machine name was changed and about
9414 * creating a new settings file if this is a new machine. */
9415 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9416 if (FAILED(rc)) return rc;
9417
9418 // keep a pointer to the current settings structures
9419 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9420 settings::MachineConfigFile *pNewConfig = NULL;
9421
9422 try
9423 {
9424 // make a fresh one to have everyone write stuff into
9425 pNewConfig = new settings::MachineConfigFile(NULL);
9426 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9427
9428 // now go and copy all the settings data from COM to the settings structures
9429 // (this calles saveSettings() on all the COM objects in the machine)
9430 copyMachineDataToSettings(*pNewConfig);
9431
9432 if (aFlags & SaveS_ResetCurStateModified)
9433 {
9434 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9435 mData->mCurrentStateModified = FALSE;
9436 fNeedsWrite = true; // always, no need to compare
9437 }
9438 else if (aFlags & SaveS_Force)
9439 {
9440 fNeedsWrite = true; // always, no need to compare
9441 }
9442 else
9443 {
9444 if (!mData->mCurrentStateModified)
9445 {
9446 // do a deep compare of the settings that we just saved with the settings
9447 // previously stored in the config file; this invokes MachineConfigFile::operator==
9448 // which does a deep compare of all the settings, which is expensive but less expensive
9449 // than writing out XML in vain
9450 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9451
9452 // could still be modified if any settings changed
9453 mData->mCurrentStateModified = fAnySettingsChanged;
9454
9455 fNeedsWrite = fAnySettingsChanged;
9456 }
9457 else
9458 fNeedsWrite = true;
9459 }
9460
9461 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9462
9463 if (fNeedsWrite)
9464 // now spit it all out!
9465 pNewConfig->write(mData->m_strConfigFileFull);
9466
9467 mData->pMachineConfigFile = pNewConfig;
9468 delete pOldConfig;
9469 commit();
9470
9471 // after saving settings, we are no longer different from the XML on disk
9472 mData->flModifications = 0;
9473 }
9474 catch (HRESULT err)
9475 {
9476 // we assume that error info is set by the thrower
9477 rc = err;
9478
9479 // restore old config
9480 delete pNewConfig;
9481 mData->pMachineConfigFile = pOldConfig;
9482 }
9483 catch (...)
9484 {
9485 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9486 }
9487
9488 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9489 {
9490 /* Fire the data change event, even on failure (since we've already
9491 * committed all data). This is done only for SessionMachines because
9492 * mutable Machine instances are always not registered (i.e. private
9493 * to the client process that creates them) and thus don't need to
9494 * inform callbacks. */
9495 if (isSessionMachine())
9496 mParent->onMachineDataChange(mData->mUuid);
9497 }
9498
9499 LogFlowThisFunc(("rc=%08X\n", rc));
9500 LogFlowThisFuncLeave();
9501 return rc;
9502}
9503
9504/**
9505 * Implementation for saving the machine settings into the given
9506 * settings::MachineConfigFile instance. This copies machine extradata
9507 * from the previous machine config file in the instance data, if any.
9508 *
9509 * This gets called from two locations:
9510 *
9511 * -- Machine::saveSettings(), during the regular XML writing;
9512 *
9513 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9514 * exported to OVF and we write the VirtualBox proprietary XML
9515 * into a <vbox:Machine> tag.
9516 *
9517 * This routine fills all the fields in there, including snapshots, *except*
9518 * for the following:
9519 *
9520 * -- fCurrentStateModified. There is some special logic associated with that.
9521 *
9522 * The caller can then call MachineConfigFile::write() or do something else
9523 * with it.
9524 *
9525 * Caller must hold the machine lock!
9526 *
9527 * This throws XML errors and HRESULT, so the caller must have a catch block!
9528 */
9529void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9530{
9531 // deep copy extradata
9532 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9533
9534 config.uuid = mData->mUuid;
9535
9536 // copy name, description, OS type, teleport, UTC etc.
9537 config.machineUserData = mUserData->s;
9538
9539 if ( mData->mMachineState == MachineState_Saved
9540 || mData->mMachineState == MachineState_Restoring
9541 // when deleting a snapshot we may or may not have a saved state in the current state,
9542 // so let's not assert here please
9543 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9544 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9545 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9546 && (!mSSData->strStateFilePath.isEmpty())
9547 )
9548 )
9549 {
9550 Assert(!mSSData->strStateFilePath.isEmpty());
9551 /* try to make the file name relative to the settings file dir */
9552 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9553 }
9554 else
9555 {
9556 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9557 config.strStateFile.setNull();
9558 }
9559
9560 if (mData->mCurrentSnapshot)
9561 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9562 else
9563 config.uuidCurrentSnapshot.clear();
9564
9565 config.timeLastStateChange = mData->mLastStateChange;
9566 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9567 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9568
9569 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9570 if (FAILED(rc)) throw rc;
9571
9572 rc = saveStorageControllers(config.storageMachine);
9573 if (FAILED(rc)) throw rc;
9574
9575 // save machine's media registry if this is VirtualBox 4.0 or later
9576 if (config.canHaveOwnMediaRegistry())
9577 {
9578 // determine machine folder
9579 Utf8Str strMachineFolder = getSettingsFileFull();
9580 strMachineFolder.stripFilename();
9581 mParent->saveMediaRegistry(config.mediaRegistry,
9582 getId(), // only media with registry ID == machine UUID
9583 strMachineFolder);
9584 // this throws HRESULT
9585 }
9586
9587 // save snapshots
9588 rc = saveAllSnapshots(config);
9589 if (FAILED(rc)) throw rc;
9590}
9591
9592/**
9593 * Saves all snapshots of the machine into the given machine config file. Called
9594 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9595 * @param config
9596 * @return
9597 */
9598HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9599{
9600 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9601
9602 HRESULT rc = S_OK;
9603
9604 try
9605 {
9606 config.llFirstSnapshot.clear();
9607
9608 if (mData->mFirstSnapshot)
9609 {
9610 settings::Snapshot snapNew;
9611 config.llFirstSnapshot.push_back(snapNew);
9612
9613 // get reference to the fresh copy of the snapshot on the list and
9614 // work on that copy directly to avoid excessive copying later
9615 settings::Snapshot &snap = config.llFirstSnapshot.front();
9616
9617 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9618 if (FAILED(rc)) throw rc;
9619 }
9620
9621// if (mType == IsSessionMachine)
9622// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9623
9624 }
9625 catch (HRESULT err)
9626 {
9627 /* we assume that error info is set by the thrower */
9628 rc = err;
9629 }
9630 catch (...)
9631 {
9632 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9633 }
9634
9635 return rc;
9636}
9637
9638/**
9639 * Saves the VM hardware configuration. It is assumed that the
9640 * given node is empty.
9641 *
9642 * @param data Reference to the settings object for the hardware config.
9643 * @param pDbg Pointer to the settings object for the debugging config
9644 * which happens to live in mHWData.
9645 * @param pAutostart Pointer to the settings object for the autostart config
9646 * which happens to live in mHWData.
9647 */
9648HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9649 settings::Autostart *pAutostart)
9650{
9651 HRESULT rc = S_OK;
9652
9653 try
9654 {
9655 /* The hardware version attribute (optional).
9656 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9657 if ( mHWData->mHWVersion == "1"
9658 && mSSData->strStateFilePath.isEmpty()
9659 )
9660 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. */
9661
9662 data.strVersion = mHWData->mHWVersion;
9663 data.uuid = mHWData->mHardwareUUID;
9664
9665 // CPU
9666 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9667 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9668 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9669 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9670 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9671 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9672 data.fPAE = !!mHWData->mPAEEnabled;
9673 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9674
9675 /* Standard and Extended CPUID leafs. */
9676 data.llCpuIdLeafs.clear();
9677 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9678 {
9679 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9680 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9681 }
9682 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9683 {
9684 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9685 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9686 }
9687
9688 data.cCPUs = mHWData->mCPUCount;
9689 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9690 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9691
9692 data.llCpus.clear();
9693 if (data.fCpuHotPlug)
9694 {
9695 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9696 {
9697 if (mHWData->mCPUAttached[idx])
9698 {
9699 settings::Cpu cpu;
9700 cpu.ulId = idx;
9701 data.llCpus.push_back(cpu);
9702 }
9703 }
9704 }
9705
9706 // memory
9707 data.ulMemorySizeMB = mHWData->mMemorySize;
9708 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9709
9710 // firmware
9711 data.firmwareType = mHWData->mFirmwareType;
9712
9713 // HID
9714 data.pointingHIDType = mHWData->mPointingHIDType;
9715 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9716
9717 // chipset
9718 data.chipsetType = mHWData->mChipsetType;
9719
9720 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9721
9722 // HPET
9723 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9724
9725 // boot order
9726 data.mapBootOrder.clear();
9727 for (size_t i = 0;
9728 i < RT_ELEMENTS(mHWData->mBootOrder);
9729 ++i)
9730 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9731
9732 // display
9733 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9734 data.cMonitors = mHWData->mMonitorCount;
9735 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9736 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9737 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9738 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9739 data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled;
9740 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9741
9742 /* VRDEServer settings (optional) */
9743 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9744 if (FAILED(rc)) throw rc;
9745
9746 /* BIOS (required) */
9747 rc = mBIOSSettings->saveSettings(data.biosSettings);
9748 if (FAILED(rc)) throw rc;
9749
9750 /* USB Controller (required) */
9751 rc = mUSBController->saveSettings(data.usbController);
9752 if (FAILED(rc)) throw rc;
9753
9754 /* Network adapters (required) */
9755 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9756 data.llNetworkAdapters.clear();
9757 /* Write out only the nominal number of network adapters for this
9758 * chipset type. Since Machine::commit() hasn't been called there
9759 * may be extra NIC settings in the vector. */
9760 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9761 {
9762 settings::NetworkAdapter nic;
9763 nic.ulSlot = slot;
9764 /* paranoia check... must not be NULL, but must not crash either. */
9765 if (mNetworkAdapters[slot])
9766 {
9767 rc = mNetworkAdapters[slot]->saveSettings(nic);
9768 if (FAILED(rc)) throw rc;
9769
9770 data.llNetworkAdapters.push_back(nic);
9771 }
9772 }
9773
9774 /* Serial ports */
9775 data.llSerialPorts.clear();
9776 for (ULONG slot = 0;
9777 slot < RT_ELEMENTS(mSerialPorts);
9778 ++slot)
9779 {
9780 settings::SerialPort s;
9781 s.ulSlot = slot;
9782 rc = mSerialPorts[slot]->saveSettings(s);
9783 if (FAILED(rc)) return rc;
9784
9785 data.llSerialPorts.push_back(s);
9786 }
9787
9788 /* Parallel ports */
9789 data.llParallelPorts.clear();
9790 for (ULONG slot = 0;
9791 slot < RT_ELEMENTS(mParallelPorts);
9792 ++slot)
9793 {
9794 settings::ParallelPort p;
9795 p.ulSlot = slot;
9796 rc = mParallelPorts[slot]->saveSettings(p);
9797 if (FAILED(rc)) return rc;
9798
9799 data.llParallelPorts.push_back(p);
9800 }
9801
9802 /* Audio adapter */
9803 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9804 if (FAILED(rc)) return rc;
9805
9806 /* Shared folders */
9807 data.llSharedFolders.clear();
9808 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9809 it != mHWData->mSharedFolders.end();
9810 ++it)
9811 {
9812 SharedFolder *pSF = *it;
9813 AutoCaller sfCaller(pSF);
9814 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9815 settings::SharedFolder sf;
9816 sf.strName = pSF->getName();
9817 sf.strHostPath = pSF->getHostPath();
9818 sf.fWritable = !!pSF->isWritable();
9819 sf.fAutoMount = !!pSF->isAutoMounted();
9820
9821 data.llSharedFolders.push_back(sf);
9822 }
9823
9824 // clipboard
9825 data.clipboardMode = mHWData->mClipboardMode;
9826
9827 // drag'n'drop
9828 data.dragAndDropMode = mHWData->mDragAndDropMode;
9829
9830 /* Guest */
9831 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9832
9833 // IO settings
9834 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9835 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9836
9837 /* BandwidthControl (required) */
9838 rc = mBandwidthControl->saveSettings(data.ioSettings);
9839 if (FAILED(rc)) throw rc;
9840
9841 /* Host PCI devices */
9842 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9843 it != mHWData->mPCIDeviceAssignments.end();
9844 ++it)
9845 {
9846 ComObjPtr<PCIDeviceAttachment> pda = *it;
9847 settings::HostPCIDeviceAttachment hpda;
9848
9849 rc = pda->saveSettings(hpda);
9850 if (FAILED(rc)) throw rc;
9851
9852 data.pciAttachments.push_back(hpda);
9853 }
9854
9855
9856 // guest properties
9857 data.llGuestProperties.clear();
9858#ifdef VBOX_WITH_GUEST_PROPS
9859 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9860 it != mHWData->mGuestProperties.end();
9861 ++it)
9862 {
9863 HWData::GuestProperty property = *it;
9864
9865 /* Remove transient guest properties at shutdown unless we
9866 * are saving state */
9867 if ( ( mData->mMachineState == MachineState_PoweredOff
9868 || mData->mMachineState == MachineState_Aborted
9869 || mData->mMachineState == MachineState_Teleported)
9870 && ( property.mFlags & guestProp::TRANSIENT
9871 || property.mFlags & guestProp::TRANSRESET))
9872 continue;
9873 settings::GuestProperty prop;
9874 prop.strName = property.strName;
9875 prop.strValue = property.strValue;
9876 prop.timestamp = property.mTimestamp;
9877 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9878 guestProp::writeFlags(property.mFlags, szFlags);
9879 prop.strFlags = szFlags;
9880
9881 data.llGuestProperties.push_back(prop);
9882 }
9883
9884 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9885 /* I presume this doesn't require a backup(). */
9886 mData->mGuestPropertiesModified = FALSE;
9887#endif /* VBOX_WITH_GUEST_PROPS defined */
9888
9889 *pDbg = mHWData->mDebugging;
9890 *pAutostart = mHWData->mAutostart;
9891 }
9892 catch(std::bad_alloc &)
9893 {
9894 return E_OUTOFMEMORY;
9895 }
9896
9897 AssertComRC(rc);
9898 return rc;
9899}
9900
9901/**
9902 * Saves the storage controller configuration.
9903 *
9904 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9905 */
9906HRESULT Machine::saveStorageControllers(settings::Storage &data)
9907{
9908 data.llStorageControllers.clear();
9909
9910 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9911 it != mStorageControllers->end();
9912 ++it)
9913 {
9914 HRESULT rc;
9915 ComObjPtr<StorageController> pCtl = *it;
9916
9917 settings::StorageController ctl;
9918 ctl.strName = pCtl->getName();
9919 ctl.controllerType = pCtl->getControllerType();
9920 ctl.storageBus = pCtl->getStorageBus();
9921 ctl.ulInstance = pCtl->getInstance();
9922 ctl.fBootable = pCtl->getBootable();
9923
9924 /* Save the port count. */
9925 ULONG portCount;
9926 rc = pCtl->COMGETTER(PortCount)(&portCount);
9927 ComAssertComRCRet(rc, rc);
9928 ctl.ulPortCount = portCount;
9929
9930 /* Save fUseHostIOCache */
9931 BOOL fUseHostIOCache;
9932 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9933 ComAssertComRCRet(rc, rc);
9934 ctl.fUseHostIOCache = !!fUseHostIOCache;
9935
9936 /* Save IDE emulation settings. */
9937 if (ctl.controllerType == StorageControllerType_IntelAhci)
9938 {
9939 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9940 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9941 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9942 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9943 )
9944 ComAssertComRCRet(rc, rc);
9945 }
9946
9947 /* save the devices now. */
9948 rc = saveStorageDevices(pCtl, ctl);
9949 ComAssertComRCRet(rc, rc);
9950
9951 data.llStorageControllers.push_back(ctl);
9952 }
9953
9954 return S_OK;
9955}
9956
9957/**
9958 * Saves the hard disk configuration.
9959 */
9960HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9961 settings::StorageController &data)
9962{
9963 MediaData::AttachmentList atts;
9964
9965 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9966 if (FAILED(rc)) return rc;
9967
9968 data.llAttachedDevices.clear();
9969 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9970 it != atts.end();
9971 ++it)
9972 {
9973 settings::AttachedDevice dev;
9974
9975 MediumAttachment *pAttach = *it;
9976 Medium *pMedium = pAttach->getMedium();
9977
9978 dev.deviceType = pAttach->getType();
9979 dev.lPort = pAttach->getPort();
9980 dev.lDevice = pAttach->getDevice();
9981 if (pMedium)
9982 {
9983 if (pMedium->isHostDrive())
9984 dev.strHostDriveSrc = pMedium->getLocationFull();
9985 else
9986 dev.uuid = pMedium->getId();
9987 dev.fPassThrough = pAttach->getPassthrough();
9988 dev.fTempEject = pAttach->getTempEject();
9989 dev.fNonRotational = pAttach->getNonRotational();
9990 dev.fDiscard = pAttach->getDiscard();
9991 }
9992
9993 dev.strBwGroup = pAttach->getBandwidthGroup();
9994
9995 data.llAttachedDevices.push_back(dev);
9996 }
9997
9998 return S_OK;
9999}
10000
10001/**
10002 * Saves machine state settings as defined by aFlags
10003 * (SaveSTS_* values).
10004 *
10005 * @param aFlags Combination of SaveSTS_* flags.
10006 *
10007 * @note Locks objects for writing.
10008 */
10009HRESULT Machine::saveStateSettings(int aFlags)
10010{
10011 if (aFlags == 0)
10012 return S_OK;
10013
10014 AutoCaller autoCaller(this);
10015 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10016
10017 /* This object's write lock is also necessary to serialize file access
10018 * (prevent concurrent reads and writes) */
10019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10020
10021 HRESULT rc = S_OK;
10022
10023 Assert(mData->pMachineConfigFile);
10024
10025 try
10026 {
10027 if (aFlags & SaveSTS_CurStateModified)
10028 mData->pMachineConfigFile->fCurrentStateModified = true;
10029
10030 if (aFlags & SaveSTS_StateFilePath)
10031 {
10032 if (!mSSData->strStateFilePath.isEmpty())
10033 /* try to make the file name relative to the settings file dir */
10034 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10035 else
10036 mData->pMachineConfigFile->strStateFile.setNull();
10037 }
10038
10039 if (aFlags & SaveSTS_StateTimeStamp)
10040 {
10041 Assert( mData->mMachineState != MachineState_Aborted
10042 || mSSData->strStateFilePath.isEmpty());
10043
10044 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10045
10046 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10047//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10048 }
10049
10050 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10051 }
10052 catch (...)
10053 {
10054 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10055 }
10056
10057 return rc;
10058}
10059
10060/**
10061 * Ensures that the given medium is added to a media registry. If this machine
10062 * was created with 4.0 or later, then the machine registry is used. Otherwise
10063 * the global VirtualBox media registry is used.
10064 *
10065 * Caller must NOT hold machine lock, media tree or any medium locks!
10066 *
10067 * @param pMedium
10068 */
10069void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10070{
10071 /* Paranoia checks: do not hold machine or media tree locks. */
10072 AssertReturnVoid(!isWriteLockOnCurrentThread());
10073 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10074
10075 ComObjPtr<Medium> pBase;
10076 {
10077 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10078 pBase = pMedium->getBase();
10079 }
10080
10081 /* Paranoia checks: do not hold medium locks. */
10082 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10083 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10084
10085 // decide which medium registry to use now that the medium is attached:
10086 Guid uuid;
10087 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10088 // machine XML is VirtualBox 4.0 or higher:
10089 uuid = getId(); // machine UUID
10090 else
10091 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10092
10093 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10094 mParent->markRegistryModified(uuid);
10095
10096 /* For more complex hard disk structures it can happen that the base
10097 * medium isn't yet associated with any medium registry. Do that now. */
10098 if (pMedium != pBase)
10099 {
10100 if (pBase->addRegistry(uuid, true /* fRecurse */))
10101 mParent->markRegistryModified(uuid);
10102 }
10103}
10104
10105/**
10106 * Creates differencing hard disks for all normal hard disks attached to this
10107 * machine and a new set of attachments to refer to created disks.
10108 *
10109 * Used when taking a snapshot or when deleting the current state. Gets called
10110 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10111 *
10112 * This method assumes that mMediaData contains the original hard disk attachments
10113 * it needs to create diffs for. On success, these attachments will be replaced
10114 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10115 * called to delete created diffs which will also rollback mMediaData and restore
10116 * whatever was backed up before calling this method.
10117 *
10118 * Attachments with non-normal hard disks are left as is.
10119 *
10120 * If @a aOnline is @c false then the original hard disks that require implicit
10121 * diffs will be locked for reading. Otherwise it is assumed that they are
10122 * already locked for writing (when the VM was started). Note that in the latter
10123 * case it is responsibility of the caller to lock the newly created diffs for
10124 * writing if this method succeeds.
10125 *
10126 * @param aProgress Progress object to run (must contain at least as
10127 * many operations left as the number of hard disks
10128 * attached).
10129 * @param aOnline Whether the VM was online prior to this operation.
10130 *
10131 * @note The progress object is not marked as completed, neither on success nor
10132 * on failure. This is a responsibility of the caller.
10133 *
10134 * @note Locks this object and the media tree for writing.
10135 */
10136HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10137 ULONG aWeight,
10138 bool aOnline)
10139{
10140 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10141
10142 AutoCaller autoCaller(this);
10143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10144
10145 AutoMultiWriteLock2 alock(this->lockHandle(),
10146 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10147
10148 /* must be in a protective state because we release the lock below */
10149 AssertReturn( mData->mMachineState == MachineState_Saving
10150 || mData->mMachineState == MachineState_LiveSnapshotting
10151 || mData->mMachineState == MachineState_RestoringSnapshot
10152 || mData->mMachineState == MachineState_DeletingSnapshot
10153 , E_FAIL);
10154
10155 HRESULT rc = S_OK;
10156
10157 // use appropriate locked media map (online or offline)
10158 MediumLockListMap lockedMediaOffline;
10159 MediumLockListMap *lockedMediaMap;
10160 if (aOnline)
10161 lockedMediaMap = &mData->mSession.mLockedMedia;
10162 else
10163 lockedMediaMap = &lockedMediaOffline;
10164
10165 try
10166 {
10167 if (!aOnline)
10168 {
10169 /* lock all attached hard disks early to detect "in use"
10170 * situations before creating actual diffs */
10171 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10172 it != mMediaData->mAttachments.end();
10173 ++it)
10174 {
10175 MediumAttachment* pAtt = *it;
10176 if (pAtt->getType() == DeviceType_HardDisk)
10177 {
10178 Medium* pMedium = pAtt->getMedium();
10179 Assert(pMedium);
10180
10181 MediumLockList *pMediumLockList(new MediumLockList());
10182 alock.release();
10183 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10184 false /* fMediumLockWrite */,
10185 NULL,
10186 *pMediumLockList);
10187 alock.acquire();
10188 if (FAILED(rc))
10189 {
10190 delete pMediumLockList;
10191 throw rc;
10192 }
10193 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10194 if (FAILED(rc))
10195 {
10196 throw setError(rc,
10197 tr("Collecting locking information for all attached media failed"));
10198 }
10199 }
10200 }
10201
10202 /* Now lock all media. If this fails, nothing is locked. */
10203 alock.release();
10204 rc = lockedMediaMap->Lock();
10205 alock.acquire();
10206 if (FAILED(rc))
10207 {
10208 throw setError(rc,
10209 tr("Locking of attached media failed"));
10210 }
10211 }
10212
10213 /* remember the current list (note that we don't use backup() since
10214 * mMediaData may be already backed up) */
10215 MediaData::AttachmentList atts = mMediaData->mAttachments;
10216
10217 /* start from scratch */
10218 mMediaData->mAttachments.clear();
10219
10220 /* go through remembered attachments and create diffs for normal hard
10221 * disks and attach them */
10222 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10223 it != atts.end();
10224 ++it)
10225 {
10226 MediumAttachment* pAtt = *it;
10227
10228 DeviceType_T devType = pAtt->getType();
10229 Medium* pMedium = pAtt->getMedium();
10230
10231 if ( devType != DeviceType_HardDisk
10232 || pMedium == NULL
10233 || pMedium->getType() != MediumType_Normal)
10234 {
10235 /* copy the attachment as is */
10236
10237 /** @todo the progress object created in Console::TakeSnaphot
10238 * only expects operations for hard disks. Later other
10239 * device types need to show up in the progress as well. */
10240 if (devType == DeviceType_HardDisk)
10241 {
10242 if (pMedium == NULL)
10243 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10244 aWeight); // weight
10245 else
10246 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10247 pMedium->getBase()->getName().c_str()).raw(),
10248 aWeight); // weight
10249 }
10250
10251 mMediaData->mAttachments.push_back(pAtt);
10252 continue;
10253 }
10254
10255 /* need a diff */
10256 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10257 pMedium->getBase()->getName().c_str()).raw(),
10258 aWeight); // weight
10259
10260 Utf8Str strFullSnapshotFolder;
10261 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10262
10263 ComObjPtr<Medium> diff;
10264 diff.createObject();
10265 // store the diff in the same registry as the parent
10266 // (this cannot fail here because we can't create implicit diffs for
10267 // unregistered images)
10268 Guid uuidRegistryParent;
10269 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10270 Assert(fInRegistry); NOREF(fInRegistry);
10271 rc = diff->init(mParent,
10272 pMedium->getPreferredDiffFormat(),
10273 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10274 uuidRegistryParent);
10275 if (FAILED(rc)) throw rc;
10276
10277 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10278 * the push_back? Looks like we're going to release medium with the
10279 * wrong kind of lock (general issue with if we fail anywhere at all)
10280 * and an orphaned VDI in the snapshots folder. */
10281
10282 /* update the appropriate lock list */
10283 MediumLockList *pMediumLockList;
10284 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10285 AssertComRCThrowRC(rc);
10286 if (aOnline)
10287 {
10288 alock.release();
10289 /* The currently attached medium will be read-only, change
10290 * the lock type to read. */
10291 rc = pMediumLockList->Update(pMedium, false);
10292 alock.acquire();
10293 AssertComRCThrowRC(rc);
10294 }
10295
10296 /* release the locks before the potentially lengthy operation */
10297 alock.release();
10298 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10299 pMediumLockList,
10300 NULL /* aProgress */,
10301 true /* aWait */);
10302 alock.acquire();
10303 if (FAILED(rc)) throw rc;
10304
10305 rc = lockedMediaMap->Unlock();
10306 AssertComRCThrowRC(rc);
10307 alock.release();
10308 rc = pMediumLockList->Append(diff, true);
10309 alock.acquire();
10310 AssertComRCThrowRC(rc);
10311 alock.release();
10312 rc = lockedMediaMap->Lock();
10313 alock.acquire();
10314 AssertComRCThrowRC(rc);
10315
10316 rc = diff->addBackReference(mData->mUuid);
10317 AssertComRCThrowRC(rc);
10318
10319 /* add a new attachment */
10320 ComObjPtr<MediumAttachment> attachment;
10321 attachment.createObject();
10322 rc = attachment->init(this,
10323 diff,
10324 pAtt->getControllerName(),
10325 pAtt->getPort(),
10326 pAtt->getDevice(),
10327 DeviceType_HardDisk,
10328 true /* aImplicit */,
10329 false /* aPassthrough */,
10330 false /* aTempEject */,
10331 pAtt->getNonRotational(),
10332 pAtt->getDiscard(),
10333 pAtt->getBandwidthGroup());
10334 if (FAILED(rc)) throw rc;
10335
10336 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10337 AssertComRCThrowRC(rc);
10338 mMediaData->mAttachments.push_back(attachment);
10339 }
10340 }
10341 catch (HRESULT aRC) { rc = aRC; }
10342
10343 /* unlock all hard disks we locked when there is no VM */
10344 if (!aOnline)
10345 {
10346 ErrorInfoKeeper eik;
10347
10348 HRESULT rc1 = lockedMediaMap->Clear();
10349 AssertComRC(rc1);
10350 }
10351
10352 return rc;
10353}
10354
10355/**
10356 * Deletes implicit differencing hard disks created either by
10357 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10358 *
10359 * Note that to delete hard disks created by #AttachDevice() this method is
10360 * called from #fixupMedia() when the changes are rolled back.
10361 *
10362 * @note Locks this object and the media tree for writing.
10363 */
10364HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10365{
10366 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10367
10368 AutoCaller autoCaller(this);
10369 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10370
10371 AutoMultiWriteLock2 alock(this->lockHandle(),
10372 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10373
10374 /* We absolutely must have backed up state. */
10375 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10376
10377 HRESULT rc = S_OK;
10378 MachineState_T oldState = mData->mMachineState;
10379
10380 /* will release the lock before the potentially lengthy operation,
10381 * so protect with the special state (unless already protected) */
10382 if ( oldState != MachineState_Saving
10383 && oldState != MachineState_LiveSnapshotting
10384 && oldState != MachineState_RestoringSnapshot
10385 && oldState != MachineState_DeletingSnapshot
10386 && oldState != MachineState_DeletingSnapshotOnline
10387 && oldState != MachineState_DeletingSnapshotPaused
10388 )
10389 setMachineState(MachineState_SettingUp);
10390
10391 // use appropriate locked media map (online or offline)
10392 MediumLockListMap lockedMediaOffline;
10393 MediumLockListMap *lockedMediaMap;
10394 if (aOnline)
10395 lockedMediaMap = &mData->mSession.mLockedMedia;
10396 else
10397 lockedMediaMap = &lockedMediaOffline;
10398
10399 try
10400 {
10401 if (!aOnline)
10402 {
10403 /* lock all attached hard disks early to detect "in use"
10404 * situations before deleting actual diffs */
10405 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10406 it != mMediaData->mAttachments.end();
10407 ++it)
10408 {
10409 MediumAttachment* pAtt = *it;
10410 if (pAtt->getType() == DeviceType_HardDisk)
10411 {
10412 Medium* pMedium = pAtt->getMedium();
10413 Assert(pMedium);
10414
10415 MediumLockList *pMediumLockList(new MediumLockList());
10416 alock.release();
10417 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10418 false /* fMediumLockWrite */,
10419 NULL,
10420 *pMediumLockList);
10421 alock.acquire();
10422
10423 if (FAILED(rc))
10424 {
10425 delete pMediumLockList;
10426 throw rc;
10427 }
10428
10429 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10430 if (FAILED(rc))
10431 throw rc;
10432 }
10433 }
10434
10435 if (FAILED(rc))
10436 throw rc;
10437 } // end of offline
10438
10439 /* Lock lists are now up to date and include implicitly created media */
10440
10441 /* Go through remembered attachments and delete all implicitly created
10442 * diffs and fix up the attachment information */
10443 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10444 MediaData::AttachmentList implicitAtts;
10445 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10446 it != mMediaData->mAttachments.end();
10447 ++it)
10448 {
10449 ComObjPtr<MediumAttachment> pAtt = *it;
10450 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10451 if (pMedium.isNull())
10452 continue;
10453
10454 // Implicit attachments go on the list for deletion and back references are removed.
10455 if (pAtt->isImplicit())
10456 {
10457 /* Deassociate and mark for deletion */
10458 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10459 rc = pMedium->removeBackReference(mData->mUuid);
10460 if (FAILED(rc))
10461 throw rc;
10462 implicitAtts.push_back(pAtt);
10463 continue;
10464 }
10465
10466 /* Was this medium attached before? */
10467 if (!findAttachment(oldAtts, pMedium))
10468 {
10469 /* no: de-associate */
10470 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10471 rc = pMedium->removeBackReference(mData->mUuid);
10472 if (FAILED(rc))
10473 throw rc;
10474 continue;
10475 }
10476 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10477 }
10478
10479 /* If there are implicit attachments to delete, throw away the lock
10480 * map contents (which will unlock all media) since the medium
10481 * attachments will be rolled back. Below we need to completely
10482 * recreate the lock map anyway since it is infinitely complex to
10483 * do this incrementally (would need reconstructing each attachment
10484 * change, which would be extremely hairy). */
10485 if (implicitAtts.size() != 0)
10486 {
10487 ErrorInfoKeeper eik;
10488
10489 HRESULT rc1 = lockedMediaMap->Clear();
10490 AssertComRC(rc1);
10491 }
10492
10493 /* rollback hard disk changes */
10494 mMediaData.rollback();
10495
10496 MultiResult mrc(S_OK);
10497
10498 // Delete unused implicit diffs.
10499 if (implicitAtts.size() != 0)
10500 {
10501 alock.release();
10502
10503 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10504 it != implicitAtts.end();
10505 ++it)
10506 {
10507 // Remove medium associated with this attachment.
10508 ComObjPtr<MediumAttachment> pAtt = *it;
10509 Assert(pAtt);
10510 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10511 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10512 Assert(pMedium);
10513
10514 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10515 // continue on delete failure, just collect error messages
10516 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10517 mrc = rc;
10518 }
10519
10520 alock.acquire();
10521
10522 /* if there is a VM recreate media lock map as mentioned above,
10523 * otherwise it is a waste of time and we leave things unlocked */
10524 if (aOnline)
10525 {
10526 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10527 /* must never be NULL, but better safe than sorry */
10528 if (!pMachine.isNull())
10529 {
10530 alock.release();
10531 rc = mData->mSession.mMachine->lockMedia();
10532 alock.acquire();
10533 if (FAILED(rc))
10534 throw rc;
10535 }
10536 }
10537 }
10538 }
10539 catch (HRESULT aRC) {rc = aRC;}
10540
10541 if (mData->mMachineState == MachineState_SettingUp)
10542 setMachineState(oldState);
10543
10544 /* unlock all hard disks we locked when there is no VM */
10545 if (!aOnline)
10546 {
10547 ErrorInfoKeeper eik;
10548
10549 HRESULT rc1 = lockedMediaMap->Clear();
10550 AssertComRC(rc1);
10551 }
10552
10553 return rc;
10554}
10555
10556
10557/**
10558 * Looks through the given list of media attachments for one with the given parameters
10559 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10560 * can be searched as well if needed.
10561 *
10562 * @param list
10563 * @param aControllerName
10564 * @param aControllerPort
10565 * @param aDevice
10566 * @return
10567 */
10568MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10569 IN_BSTR aControllerName,
10570 LONG aControllerPort,
10571 LONG aDevice)
10572{
10573 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10574 it != ll.end();
10575 ++it)
10576 {
10577 MediumAttachment *pAttach = *it;
10578 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10579 return pAttach;
10580 }
10581
10582 return NULL;
10583}
10584
10585/**
10586 * Looks through the given list of media attachments for one with the given parameters
10587 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10588 * can be searched as well if needed.
10589 *
10590 * @param list
10591 * @param aControllerName
10592 * @param aControllerPort
10593 * @param aDevice
10594 * @return
10595 */
10596MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10597 ComObjPtr<Medium> pMedium)
10598{
10599 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10600 it != ll.end();
10601 ++it)
10602 {
10603 MediumAttachment *pAttach = *it;
10604 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10605 if (pMediumThis == pMedium)
10606 return pAttach;
10607 }
10608
10609 return NULL;
10610}
10611
10612/**
10613 * Looks through the given list of media attachments for one with the given parameters
10614 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10615 * can be searched as well if needed.
10616 *
10617 * @param list
10618 * @param aControllerName
10619 * @param aControllerPort
10620 * @param aDevice
10621 * @return
10622 */
10623MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10624 Guid &id)
10625{
10626 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10627 it != ll.end();
10628 ++it)
10629 {
10630 MediumAttachment *pAttach = *it;
10631 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10632 if (pMediumThis->getId() == id)
10633 return pAttach;
10634 }
10635
10636 return NULL;
10637}
10638
10639/**
10640 * Main implementation for Machine::DetachDevice. This also gets called
10641 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10642 *
10643 * @param pAttach Medium attachment to detach.
10644 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10645 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10646 * @return
10647 */
10648HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10649 AutoWriteLock &writeLock,
10650 Snapshot *pSnapshot)
10651{
10652 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10653 DeviceType_T mediumType = pAttach->getType();
10654
10655 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10656
10657 if (pAttach->isImplicit())
10658 {
10659 /* attempt to implicitly delete the implicitly created diff */
10660
10661 /// @todo move the implicit flag from MediumAttachment to Medium
10662 /// and forbid any hard disk operation when it is implicit. Or maybe
10663 /// a special media state for it to make it even more simple.
10664
10665 Assert(mMediaData.isBackedUp());
10666
10667 /* will release the lock before the potentially lengthy operation, so
10668 * protect with the special state */
10669 MachineState_T oldState = mData->mMachineState;
10670 setMachineState(MachineState_SettingUp);
10671
10672 writeLock.release();
10673
10674 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10675 true /*aWait*/);
10676
10677 writeLock.acquire();
10678
10679 setMachineState(oldState);
10680
10681 if (FAILED(rc)) return rc;
10682 }
10683
10684 setModified(IsModified_Storage);
10685 mMediaData.backup();
10686 mMediaData->mAttachments.remove(pAttach);
10687
10688 if (!oldmedium.isNull())
10689 {
10690 // if this is from a snapshot, do not defer detachment to commitMedia()
10691 if (pSnapshot)
10692 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10693 // else if non-hard disk media, do not defer detachment to commitMedia() either
10694 else if (mediumType != DeviceType_HardDisk)
10695 oldmedium->removeBackReference(mData->mUuid);
10696 }
10697
10698 return S_OK;
10699}
10700
10701/**
10702 * Goes thru all media of the given list and
10703 *
10704 * 1) calls detachDevice() on each of them for this machine and
10705 * 2) adds all Medium objects found in the process to the given list,
10706 * depending on cleanupMode.
10707 *
10708 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10709 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10710 * media to the list.
10711 *
10712 * This gets called from Machine::Unregister, both for the actual Machine and
10713 * the SnapshotMachine objects that might be found in the snapshots.
10714 *
10715 * Requires caller and locking. The machine lock must be passed in because it
10716 * will be passed on to detachDevice which needs it for temporary unlocking.
10717 *
10718 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10719 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10720 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10721 * otherwise no media get added.
10722 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10723 * @return
10724 */
10725HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10726 Snapshot *pSnapshot,
10727 CleanupMode_T cleanupMode,
10728 MediaList &llMedia)
10729{
10730 Assert(isWriteLockOnCurrentThread());
10731
10732 HRESULT rc;
10733
10734 // make a temporary list because detachDevice invalidates iterators into
10735 // mMediaData->mAttachments
10736 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10737
10738 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10739 it != llAttachments2.end();
10740 ++it)
10741 {
10742 ComObjPtr<MediumAttachment> &pAttach = *it;
10743 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10744
10745 if (!pMedium.isNull())
10746 {
10747 AutoCaller mac(pMedium);
10748 if (FAILED(mac.rc())) return mac.rc();
10749 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10750 DeviceType_T devType = pMedium->getDeviceType();
10751 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10752 && devType == DeviceType_HardDisk)
10753 || (cleanupMode == CleanupMode_Full)
10754 )
10755 {
10756 llMedia.push_back(pMedium);
10757 ComObjPtr<Medium> pParent = pMedium->getParent();
10758 /*
10759 * Search for medias which are not attached to any machine, but
10760 * in the chain to an attached disk. Mediums are only consided
10761 * if they are:
10762 * - have only one child
10763 * - no references to any machines
10764 * - are of normal medium type
10765 */
10766 while (!pParent.isNull())
10767 {
10768 AutoCaller mac1(pParent);
10769 if (FAILED(mac1.rc())) return mac1.rc();
10770 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10771 if (pParent->getChildren().size() == 1)
10772 {
10773 if ( pParent->getMachineBackRefCount() == 0
10774 && pParent->getType() == MediumType_Normal
10775 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10776 llMedia.push_back(pParent);
10777 }
10778 else
10779 break;
10780 pParent = pParent->getParent();
10781 }
10782 }
10783 }
10784
10785 // real machine: then we need to use the proper method
10786 rc = detachDevice(pAttach, writeLock, pSnapshot);
10787
10788 if (FAILED(rc))
10789 return rc;
10790 }
10791
10792 return S_OK;
10793}
10794
10795/**
10796 * Perform deferred hard disk detachments.
10797 *
10798 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10799 * backed up).
10800 *
10801 * If @a aOnline is @c true then this method will also unlock the old hard disks
10802 * for which the new implicit diffs were created and will lock these new diffs for
10803 * writing.
10804 *
10805 * @param aOnline Whether the VM was online prior to this operation.
10806 *
10807 * @note Locks this object for writing!
10808 */
10809void Machine::commitMedia(bool aOnline /*= false*/)
10810{
10811 AutoCaller autoCaller(this);
10812 AssertComRCReturnVoid(autoCaller.rc());
10813
10814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10815
10816 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10817
10818 HRESULT rc = S_OK;
10819
10820 /* no attach/detach operations -- nothing to do */
10821 if (!mMediaData.isBackedUp())
10822 return;
10823
10824 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10825 bool fMediaNeedsLocking = false;
10826
10827 /* enumerate new attachments */
10828 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10829 it != mMediaData->mAttachments.end();
10830 ++it)
10831 {
10832 MediumAttachment *pAttach = *it;
10833
10834 pAttach->commit();
10835
10836 Medium* pMedium = pAttach->getMedium();
10837 bool fImplicit = pAttach->isImplicit();
10838
10839 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10840 (pMedium) ? pMedium->getName().c_str() : "NULL",
10841 fImplicit));
10842
10843 /** @todo convert all this Machine-based voodoo to MediumAttachment
10844 * based commit logic. */
10845 if (fImplicit)
10846 {
10847 /* convert implicit attachment to normal */
10848 pAttach->setImplicit(false);
10849
10850 if ( aOnline
10851 && pMedium
10852 && pAttach->getType() == DeviceType_HardDisk
10853 )
10854 {
10855 ComObjPtr<Medium> parent = pMedium->getParent();
10856 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10857
10858 /* update the appropriate lock list */
10859 MediumLockList *pMediumLockList;
10860 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10861 AssertComRC(rc);
10862 if (pMediumLockList)
10863 {
10864 /* unlock if there's a need to change the locking */
10865 if (!fMediaNeedsLocking)
10866 {
10867 rc = mData->mSession.mLockedMedia.Unlock();
10868 AssertComRC(rc);
10869 fMediaNeedsLocking = true;
10870 }
10871 rc = pMediumLockList->Update(parent, false);
10872 AssertComRC(rc);
10873 rc = pMediumLockList->Append(pMedium, true);
10874 AssertComRC(rc);
10875 }
10876 }
10877
10878 continue;
10879 }
10880
10881 if (pMedium)
10882 {
10883 /* was this medium attached before? */
10884 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10885 oldIt != oldAtts.end();
10886 ++oldIt)
10887 {
10888 MediumAttachment *pOldAttach = *oldIt;
10889 if (pOldAttach->getMedium() == pMedium)
10890 {
10891 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10892
10893 /* yes: remove from old to avoid de-association */
10894 oldAtts.erase(oldIt);
10895 break;
10896 }
10897 }
10898 }
10899 }
10900
10901 /* enumerate remaining old attachments and de-associate from the
10902 * current machine state */
10903 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10904 it != oldAtts.end();
10905 ++it)
10906 {
10907 MediumAttachment *pAttach = *it;
10908 Medium* pMedium = pAttach->getMedium();
10909
10910 /* Detach only hard disks, since DVD/floppy media is detached
10911 * instantly in MountMedium. */
10912 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10913 {
10914 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10915
10916 /* now de-associate from the current machine state */
10917 rc = pMedium->removeBackReference(mData->mUuid);
10918 AssertComRC(rc);
10919
10920 if (aOnline)
10921 {
10922 /* unlock since medium is not used anymore */
10923 MediumLockList *pMediumLockList;
10924 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10925 AssertComRC(rc);
10926 if (pMediumLockList)
10927 {
10928 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10929 AssertComRC(rc);
10930 }
10931 }
10932 }
10933 }
10934
10935 /* take media locks again so that the locking state is consistent */
10936 if (fMediaNeedsLocking)
10937 {
10938 Assert(aOnline);
10939 rc = mData->mSession.mLockedMedia.Lock();
10940 AssertComRC(rc);
10941 }
10942
10943 /* commit the hard disk changes */
10944 mMediaData.commit();
10945
10946 if (isSessionMachine())
10947 {
10948 /*
10949 * Update the parent machine to point to the new owner.
10950 * This is necessary because the stored parent will point to the
10951 * session machine otherwise and cause crashes or errors later
10952 * when the session machine gets invalid.
10953 */
10954 /** @todo Change the MediumAttachment class to behave like any other
10955 * class in this regard by creating peer MediumAttachment
10956 * objects for session machines and share the data with the peer
10957 * machine.
10958 */
10959 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10960 it != mMediaData->mAttachments.end();
10961 ++it)
10962 {
10963 (*it)->updateParentMachine(mPeer);
10964 }
10965
10966 /* attach new data to the primary machine and reshare it */
10967 mPeer->mMediaData.attach(mMediaData);
10968 }
10969
10970 return;
10971}
10972
10973/**
10974 * Perform deferred deletion of implicitly created diffs.
10975 *
10976 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10977 * backed up).
10978 *
10979 * @note Locks this object for writing!
10980 */
10981void Machine::rollbackMedia()
10982{
10983 AutoCaller autoCaller(this);
10984 AssertComRCReturnVoid(autoCaller.rc());
10985
10986 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10987 LogFlowThisFunc(("Entering rollbackMedia\n"));
10988
10989 HRESULT rc = S_OK;
10990
10991 /* no attach/detach operations -- nothing to do */
10992 if (!mMediaData.isBackedUp())
10993 return;
10994
10995 /* enumerate new attachments */
10996 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10997 it != mMediaData->mAttachments.end();
10998 ++it)
10999 {
11000 MediumAttachment *pAttach = *it;
11001 /* Fix up the backrefs for DVD/floppy media. */
11002 if (pAttach->getType() != DeviceType_HardDisk)
11003 {
11004 Medium* pMedium = pAttach->getMedium();
11005 if (pMedium)
11006 {
11007 rc = pMedium->removeBackReference(mData->mUuid);
11008 AssertComRC(rc);
11009 }
11010 }
11011
11012 (*it)->rollback();
11013
11014 pAttach = *it;
11015 /* Fix up the backrefs for DVD/floppy media. */
11016 if (pAttach->getType() != DeviceType_HardDisk)
11017 {
11018 Medium* pMedium = pAttach->getMedium();
11019 if (pMedium)
11020 {
11021 rc = pMedium->addBackReference(mData->mUuid);
11022 AssertComRC(rc);
11023 }
11024 }
11025 }
11026
11027 /** @todo convert all this Machine-based voodoo to MediumAttachment
11028 * based rollback logic. */
11029 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11030
11031 return;
11032}
11033
11034/**
11035 * Returns true if the settings file is located in the directory named exactly
11036 * as the machine; this means, among other things, that the machine directory
11037 * should be auto-renamed.
11038 *
11039 * @param aSettingsDir if not NULL, the full machine settings file directory
11040 * name will be assigned there.
11041 *
11042 * @note Doesn't lock anything.
11043 * @note Not thread safe (must be called from this object's lock).
11044 */
11045bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11046{
11047 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11048 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11049 if (aSettingsDir)
11050 *aSettingsDir = strMachineDirName;
11051 strMachineDirName.stripPath(); // vmname
11052 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11053 strConfigFileOnly.stripPath() // vmname.vbox
11054 .stripExt(); // vmname
11055 /** @todo hack, make somehow use of ComposeMachineFilename */
11056 if (mUserData->s.fDirectoryIncludesUUID)
11057 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11058
11059 AssertReturn(!strMachineDirName.isEmpty(), false);
11060 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11061
11062 return strMachineDirName == strConfigFileOnly;
11063}
11064
11065/**
11066 * Discards all changes to machine settings.
11067 *
11068 * @param aNotify Whether to notify the direct session about changes or not.
11069 *
11070 * @note Locks objects for writing!
11071 */
11072void Machine::rollback(bool aNotify)
11073{
11074 AutoCaller autoCaller(this);
11075 AssertComRCReturn(autoCaller.rc(), (void)0);
11076
11077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11078
11079 if (!mStorageControllers.isNull())
11080 {
11081 if (mStorageControllers.isBackedUp())
11082 {
11083 /* unitialize all new devices (absent in the backed up list). */
11084 StorageControllerList::const_iterator it = mStorageControllers->begin();
11085 StorageControllerList *backedList = mStorageControllers.backedUpData();
11086 while (it != mStorageControllers->end())
11087 {
11088 if ( std::find(backedList->begin(), backedList->end(), *it)
11089 == backedList->end()
11090 )
11091 {
11092 (*it)->uninit();
11093 }
11094 ++it;
11095 }
11096
11097 /* restore the list */
11098 mStorageControllers.rollback();
11099 }
11100
11101 /* rollback any changes to devices after restoring the list */
11102 if (mData->flModifications & IsModified_Storage)
11103 {
11104 StorageControllerList::const_iterator it = mStorageControllers->begin();
11105 while (it != mStorageControllers->end())
11106 {
11107 (*it)->rollback();
11108 ++it;
11109 }
11110 }
11111 }
11112
11113 mUserData.rollback();
11114
11115 mHWData.rollback();
11116
11117 if (mData->flModifications & IsModified_Storage)
11118 rollbackMedia();
11119
11120 if (mBIOSSettings)
11121 mBIOSSettings->rollback();
11122
11123 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11124 mVRDEServer->rollback();
11125
11126 if (mAudioAdapter)
11127 mAudioAdapter->rollback();
11128
11129 if (mUSBController && (mData->flModifications & IsModified_USB))
11130 mUSBController->rollback();
11131
11132 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11133 mBandwidthControl->rollback();
11134
11135 if (!mHWData.isNull())
11136 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11137 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11138 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11139 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11140
11141 if (mData->flModifications & IsModified_NetworkAdapters)
11142 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11143 if ( mNetworkAdapters[slot]
11144 && mNetworkAdapters[slot]->isModified())
11145 {
11146 mNetworkAdapters[slot]->rollback();
11147 networkAdapters[slot] = mNetworkAdapters[slot];
11148 }
11149
11150 if (mData->flModifications & IsModified_SerialPorts)
11151 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11152 if ( mSerialPorts[slot]
11153 && mSerialPorts[slot]->isModified())
11154 {
11155 mSerialPorts[slot]->rollback();
11156 serialPorts[slot] = mSerialPorts[slot];
11157 }
11158
11159 if (mData->flModifications & IsModified_ParallelPorts)
11160 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11161 if ( mParallelPorts[slot]
11162 && mParallelPorts[slot]->isModified())
11163 {
11164 mParallelPorts[slot]->rollback();
11165 parallelPorts[slot] = mParallelPorts[slot];
11166 }
11167
11168 if (aNotify)
11169 {
11170 /* inform the direct session about changes */
11171
11172 ComObjPtr<Machine> that = this;
11173 uint32_t flModifications = mData->flModifications;
11174 alock.release();
11175
11176 if (flModifications & IsModified_SharedFolders)
11177 that->onSharedFolderChange();
11178
11179 if (flModifications & IsModified_VRDEServer)
11180 that->onVRDEServerChange(/* aRestart */ TRUE);
11181 if (flModifications & IsModified_USB)
11182 that->onUSBControllerChange();
11183
11184 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11185 if (networkAdapters[slot])
11186 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11187 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11188 if (serialPorts[slot])
11189 that->onSerialPortChange(serialPorts[slot]);
11190 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11191 if (parallelPorts[slot])
11192 that->onParallelPortChange(parallelPorts[slot]);
11193
11194 if (flModifications & IsModified_Storage)
11195 that->onStorageControllerChange();
11196
11197#if 0
11198 if (flModifications & IsModified_BandwidthControl)
11199 that->onBandwidthControlChange();
11200#endif
11201 }
11202}
11203
11204/**
11205 * Commits all the changes to machine settings.
11206 *
11207 * Note that this operation is supposed to never fail.
11208 *
11209 * @note Locks this object and children for writing.
11210 */
11211void Machine::commit()
11212{
11213 AutoCaller autoCaller(this);
11214 AssertComRCReturnVoid(autoCaller.rc());
11215
11216 AutoCaller peerCaller(mPeer);
11217 AssertComRCReturnVoid(peerCaller.rc());
11218
11219 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11220
11221 /*
11222 * use safe commit to ensure Snapshot machines (that share mUserData)
11223 * will still refer to a valid memory location
11224 */
11225 mUserData.commitCopy();
11226
11227 mHWData.commit();
11228
11229 if (mMediaData.isBackedUp())
11230 commitMedia();
11231
11232 mBIOSSettings->commit();
11233 mVRDEServer->commit();
11234 mAudioAdapter->commit();
11235 mUSBController->commit();
11236 mBandwidthControl->commit();
11237
11238 /* Since mNetworkAdapters is a list which might have been changed (resized)
11239 * without using the Backupable<> template we need to handle the copying
11240 * of the list entries manually, including the creation of peers for the
11241 * new objects. */
11242 bool commitNetworkAdapters = false;
11243 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11244 if (mPeer)
11245 {
11246 /* commit everything, even the ones which will go away */
11247 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11248 mNetworkAdapters[slot]->commit();
11249 /* copy over the new entries, creating a peer and uninit the original */
11250 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11251 for (size_t slot = 0; slot < newSize; slot++)
11252 {
11253 /* look if this adapter has a peer device */
11254 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11255 if (!peer)
11256 {
11257 /* no peer means the adapter is a newly created one;
11258 * create a peer owning data this data share it with */
11259 peer.createObject();
11260 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11261 }
11262 mPeer->mNetworkAdapters[slot] = peer;
11263 }
11264 /* uninit any no longer needed network adapters */
11265 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11266 mNetworkAdapters[slot]->uninit();
11267 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11268 {
11269 if (mPeer->mNetworkAdapters[slot])
11270 mPeer->mNetworkAdapters[slot]->uninit();
11271 }
11272 /* Keep the original network adapter count until this point, so that
11273 * discarding a chipset type change will not lose settings. */
11274 mNetworkAdapters.resize(newSize);
11275 mPeer->mNetworkAdapters.resize(newSize);
11276 }
11277 else
11278 {
11279 /* we have no peer (our parent is the newly created machine);
11280 * just commit changes to the network adapters */
11281 commitNetworkAdapters = true;
11282 }
11283 if (commitNetworkAdapters)
11284 {
11285 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11286 mNetworkAdapters[slot]->commit();
11287 }
11288
11289 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11290 mSerialPorts[slot]->commit();
11291 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11292 mParallelPorts[slot]->commit();
11293
11294 bool commitStorageControllers = false;
11295
11296 if (mStorageControllers.isBackedUp())
11297 {
11298 mStorageControllers.commit();
11299
11300 if (mPeer)
11301 {
11302 /* Commit all changes to new controllers (this will reshare data with
11303 * peers for those who have peers) */
11304 StorageControllerList *newList = new StorageControllerList();
11305 StorageControllerList::const_iterator it = mStorageControllers->begin();
11306 while (it != mStorageControllers->end())
11307 {
11308 (*it)->commit();
11309
11310 /* look if this controller has a peer device */
11311 ComObjPtr<StorageController> peer = (*it)->getPeer();
11312 if (!peer)
11313 {
11314 /* no peer means the device is a newly created one;
11315 * create a peer owning data this device share it with */
11316 peer.createObject();
11317 peer->init(mPeer, *it, true /* aReshare */);
11318 }
11319 else
11320 {
11321 /* remove peer from the old list */
11322 mPeer->mStorageControllers->remove(peer);
11323 }
11324 /* and add it to the new list */
11325 newList->push_back(peer);
11326
11327 ++it;
11328 }
11329
11330 /* uninit old peer's controllers that are left */
11331 it = mPeer->mStorageControllers->begin();
11332 while (it != mPeer->mStorageControllers->end())
11333 {
11334 (*it)->uninit();
11335 ++it;
11336 }
11337
11338 /* attach new list of controllers to our peer */
11339 mPeer->mStorageControllers.attach(newList);
11340 }
11341 else
11342 {
11343 /* we have no peer (our parent is the newly created machine);
11344 * just commit changes to devices */
11345 commitStorageControllers = true;
11346 }
11347 }
11348 else
11349 {
11350 /* the list of controllers itself is not changed,
11351 * just commit changes to controllers themselves */
11352 commitStorageControllers = true;
11353 }
11354
11355 if (commitStorageControllers)
11356 {
11357 StorageControllerList::const_iterator it = mStorageControllers->begin();
11358 while (it != mStorageControllers->end())
11359 {
11360 (*it)->commit();
11361 ++it;
11362 }
11363 }
11364
11365 if (isSessionMachine())
11366 {
11367 /* attach new data to the primary machine and reshare it */
11368 mPeer->mUserData.attach(mUserData);
11369 mPeer->mHWData.attach(mHWData);
11370 /* mMediaData is reshared by fixupMedia */
11371 // mPeer->mMediaData.attach(mMediaData);
11372 Assert(mPeer->mMediaData.data() == mMediaData.data());
11373 }
11374}
11375
11376/**
11377 * Copies all the hardware data from the given machine.
11378 *
11379 * Currently, only called when the VM is being restored from a snapshot. In
11380 * particular, this implies that the VM is not running during this method's
11381 * call.
11382 *
11383 * @note This method must be called from under this object's lock.
11384 *
11385 * @note This method doesn't call #commit(), so all data remains backed up and
11386 * unsaved.
11387 */
11388void Machine::copyFrom(Machine *aThat)
11389{
11390 AssertReturnVoid(!isSnapshotMachine());
11391 AssertReturnVoid(aThat->isSnapshotMachine());
11392
11393 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11394
11395 mHWData.assignCopy(aThat->mHWData);
11396
11397 // create copies of all shared folders (mHWData after attaching a copy
11398 // contains just references to original objects)
11399 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11400 it != mHWData->mSharedFolders.end();
11401 ++it)
11402 {
11403 ComObjPtr<SharedFolder> folder;
11404 folder.createObject();
11405 HRESULT rc = folder->initCopy(getMachine(), *it);
11406 AssertComRC(rc);
11407 *it = folder;
11408 }
11409
11410 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11411 mVRDEServer->copyFrom(aThat->mVRDEServer);
11412 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11413 mUSBController->copyFrom(aThat->mUSBController);
11414 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11415
11416 /* create private copies of all controllers */
11417 mStorageControllers.backup();
11418 mStorageControllers->clear();
11419 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11420 it != aThat->mStorageControllers->end();
11421 ++it)
11422 {
11423 ComObjPtr<StorageController> ctrl;
11424 ctrl.createObject();
11425 ctrl->initCopy(this, *it);
11426 mStorageControllers->push_back(ctrl);
11427 }
11428
11429 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11430 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11431 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11432 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11433 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11434 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11435 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11436}
11437
11438/**
11439 * Returns whether the given storage controller is hotplug capable.
11440 *
11441 * @returns true if the controller supports hotplugging
11442 * false otherwise.
11443 * @param enmCtrlType The controller type to check for.
11444 */
11445bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11446{
11447 switch (enmCtrlType)
11448 {
11449 case StorageControllerType_IntelAhci:
11450 return true;
11451 case StorageControllerType_LsiLogic:
11452 case StorageControllerType_LsiLogicSas:
11453 case StorageControllerType_BusLogic:
11454 case StorageControllerType_PIIX3:
11455 case StorageControllerType_PIIX4:
11456 case StorageControllerType_ICH6:
11457 case StorageControllerType_I82078:
11458 default:
11459 return false;
11460 }
11461}
11462
11463#ifdef VBOX_WITH_RESOURCE_USAGE_API
11464
11465void Machine::getDiskList(MediaList &list)
11466{
11467 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11468 it != mMediaData->mAttachments.end();
11469 ++it)
11470 {
11471 MediumAttachment* pAttach = *it;
11472 /* just in case */
11473 AssertStmt(pAttach, continue);
11474
11475 AutoCaller localAutoCallerA(pAttach);
11476 if (FAILED(localAutoCallerA.rc())) continue;
11477
11478 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11479
11480 if (pAttach->getType() == DeviceType_HardDisk)
11481 list.push_back(pAttach->getMedium());
11482 }
11483}
11484
11485void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11486{
11487 AssertReturnVoid(isWriteLockOnCurrentThread());
11488 AssertPtrReturnVoid(aCollector);
11489
11490 pm::CollectorHAL *hal = aCollector->getHAL();
11491 /* Create sub metrics */
11492 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11493 "Percentage of processor time spent in user mode by the VM process.");
11494 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11495 "Percentage of processor time spent in kernel mode by the VM process.");
11496 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11497 "Size of resident portion of VM process in memory.");
11498 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11499 "Actual size of all VM disks combined.");
11500 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11501 "Network receive rate.");
11502 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11503 "Network transmit rate.");
11504 /* Create and register base metrics */
11505 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11506 cpuLoadUser, cpuLoadKernel);
11507 aCollector->registerBaseMetric(cpuLoad);
11508 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11509 ramUsageUsed);
11510 aCollector->registerBaseMetric(ramUsage);
11511 MediaList disks;
11512 getDiskList(disks);
11513 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11514 diskUsageUsed);
11515 aCollector->registerBaseMetric(diskUsage);
11516
11517 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11518 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11519 new pm::AggregateAvg()));
11520 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11521 new pm::AggregateMin()));
11522 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11523 new pm::AggregateMax()));
11524 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11525 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11526 new pm::AggregateAvg()));
11527 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11528 new pm::AggregateMin()));
11529 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11530 new pm::AggregateMax()));
11531
11532 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11533 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11534 new pm::AggregateAvg()));
11535 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11536 new pm::AggregateMin()));
11537 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11538 new pm::AggregateMax()));
11539
11540 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11541 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11542 new pm::AggregateAvg()));
11543 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11544 new pm::AggregateMin()));
11545 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11546 new pm::AggregateMax()));
11547
11548
11549 /* Guest metrics collector */
11550 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11551 aCollector->registerGuest(mCollectorGuest);
11552 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11553 this, __PRETTY_FUNCTION__, mCollectorGuest));
11554
11555 /* Create sub metrics */
11556 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11557 "Percentage of processor time spent in user mode as seen by the guest.");
11558 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11559 "Percentage of processor time spent in kernel mode as seen by the guest.");
11560 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11561 "Percentage of processor time spent idling as seen by the guest.");
11562
11563 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11564 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11565 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11566 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11567 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11568 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11569
11570 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11571
11572 /* Create and register base metrics */
11573 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11574 machineNetRx, machineNetTx);
11575 aCollector->registerBaseMetric(machineNetRate);
11576
11577 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11578 guestLoadUser, guestLoadKernel, guestLoadIdle);
11579 aCollector->registerBaseMetric(guestCpuLoad);
11580
11581 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11582 guestMemTotal, guestMemFree,
11583 guestMemBalloon, guestMemShared,
11584 guestMemCache, guestPagedTotal);
11585 aCollector->registerBaseMetric(guestCpuMem);
11586
11587 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11588 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11589 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11590 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11591
11592 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11593 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11594 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11595 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11596
11597 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11598 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11599 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11600 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11601
11602 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11603 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11604 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11605 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11606
11607 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11608 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11609 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11610 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11611
11612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11613 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11616
11617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11618 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11621
11622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11623 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11624 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11625 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11626
11627 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11628 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11631
11632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11633 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11634 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11635 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11636
11637 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11638 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11639 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11640 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11641}
11642
11643void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11644{
11645 AssertReturnVoid(isWriteLockOnCurrentThread());
11646
11647 if (aCollector)
11648 {
11649 aCollector->unregisterMetricsFor(aMachine);
11650 aCollector->unregisterBaseMetricsFor(aMachine);
11651 }
11652}
11653
11654#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11655
11656
11657////////////////////////////////////////////////////////////////////////////////
11658
11659DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11660
11661HRESULT SessionMachine::FinalConstruct()
11662{
11663 LogFlowThisFunc(("\n"));
11664
11665#if defined(RT_OS_WINDOWS)
11666 mIPCSem = NULL;
11667#elif defined(RT_OS_OS2)
11668 mIPCSem = NULLHANDLE;
11669#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11670 mIPCSem = -1;
11671#else
11672# error "Port me!"
11673#endif
11674
11675 return BaseFinalConstruct();
11676}
11677
11678void SessionMachine::FinalRelease()
11679{
11680 LogFlowThisFunc(("\n"));
11681
11682 uninit(Uninit::Unexpected);
11683
11684 BaseFinalRelease();
11685}
11686
11687/**
11688 * @note Must be called only by Machine::openSession() from its own write lock.
11689 */
11690HRESULT SessionMachine::init(Machine *aMachine)
11691{
11692 LogFlowThisFuncEnter();
11693 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11694
11695 AssertReturn(aMachine, E_INVALIDARG);
11696
11697 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11698
11699 /* Enclose the state transition NotReady->InInit->Ready */
11700 AutoInitSpan autoInitSpan(this);
11701 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11702
11703 /* create the interprocess semaphore */
11704#if defined(RT_OS_WINDOWS)
11705 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11706 for (size_t i = 0; i < mIPCSemName.length(); i++)
11707 if (mIPCSemName.raw()[i] == '\\')
11708 mIPCSemName.raw()[i] = '/';
11709 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11710 ComAssertMsgRet(mIPCSem,
11711 ("Cannot create IPC mutex '%ls', err=%d",
11712 mIPCSemName.raw(), ::GetLastError()),
11713 E_FAIL);
11714#elif defined(RT_OS_OS2)
11715 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11716 aMachine->mData->mUuid.raw());
11717 mIPCSemName = ipcSem;
11718 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11719 ComAssertMsgRet(arc == NO_ERROR,
11720 ("Cannot create IPC mutex '%s', arc=%ld",
11721 ipcSem.c_str(), arc),
11722 E_FAIL);
11723#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11724# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11725# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11726 /** @todo Check that this still works correctly. */
11727 AssertCompileSize(key_t, 8);
11728# else
11729 AssertCompileSize(key_t, 4);
11730# endif
11731 key_t key;
11732 mIPCSem = -1;
11733 mIPCKey = "0";
11734 for (uint32_t i = 0; i < 1 << 24; i++)
11735 {
11736 key = ((uint32_t)'V' << 24) | i;
11737 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11738 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11739 {
11740 mIPCSem = sem;
11741 if (sem >= 0)
11742 mIPCKey = BstrFmt("%u", key);
11743 break;
11744 }
11745 }
11746# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11747 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11748 char *pszSemName = NULL;
11749 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11750 key_t key = ::ftok(pszSemName, 'V');
11751 RTStrFree(pszSemName);
11752
11753 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11754# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11755
11756 int errnoSave = errno;
11757 if (mIPCSem < 0 && errnoSave == ENOSYS)
11758 {
11759 setError(E_FAIL,
11760 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11761 "support for SysV IPC. Check the host kernel configuration for "
11762 "CONFIG_SYSVIPC=y"));
11763 return E_FAIL;
11764 }
11765 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11766 * the IPC semaphores */
11767 if (mIPCSem < 0 && errnoSave == ENOSPC)
11768 {
11769#ifdef RT_OS_LINUX
11770 setError(E_FAIL,
11771 tr("Cannot create IPC semaphore because the system limit for the "
11772 "maximum number of semaphore sets (SEMMNI), or the system wide "
11773 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11774 "current set of SysV IPC semaphores can be determined from "
11775 "the file /proc/sysvipc/sem"));
11776#else
11777 setError(E_FAIL,
11778 tr("Cannot create IPC semaphore because the system-imposed limit "
11779 "on the maximum number of allowed semaphores or semaphore "
11780 "identifiers system-wide would be exceeded"));
11781#endif
11782 return E_FAIL;
11783 }
11784 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11785 E_FAIL);
11786 /* set the initial value to 1 */
11787 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11788 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11789 E_FAIL);
11790#else
11791# error "Port me!"
11792#endif
11793
11794 /* memorize the peer Machine */
11795 unconst(mPeer) = aMachine;
11796 /* share the parent pointer */
11797 unconst(mParent) = aMachine->mParent;
11798
11799 /* take the pointers to data to share */
11800 mData.share(aMachine->mData);
11801 mSSData.share(aMachine->mSSData);
11802
11803 mUserData.share(aMachine->mUserData);
11804 mHWData.share(aMachine->mHWData);
11805 mMediaData.share(aMachine->mMediaData);
11806
11807 mStorageControllers.allocate();
11808 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11809 it != aMachine->mStorageControllers->end();
11810 ++it)
11811 {
11812 ComObjPtr<StorageController> ctl;
11813 ctl.createObject();
11814 ctl->init(this, *it);
11815 mStorageControllers->push_back(ctl);
11816 }
11817
11818 unconst(mBIOSSettings).createObject();
11819 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11820 /* create another VRDEServer object that will be mutable */
11821 unconst(mVRDEServer).createObject();
11822 mVRDEServer->init(this, aMachine->mVRDEServer);
11823 /* create another audio adapter object that will be mutable */
11824 unconst(mAudioAdapter).createObject();
11825 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11826 /* create a list of serial ports that will be mutable */
11827 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11828 {
11829 unconst(mSerialPorts[slot]).createObject();
11830 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11831 }
11832 /* create a list of parallel ports that will be mutable */
11833 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11834 {
11835 unconst(mParallelPorts[slot]).createObject();
11836 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11837 }
11838 /* create another USB controller object that will be mutable */
11839 unconst(mUSBController).createObject();
11840 mUSBController->init(this, aMachine->mUSBController);
11841
11842 /* create a list of network adapters that will be mutable */
11843 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11844 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11845 {
11846 unconst(mNetworkAdapters[slot]).createObject();
11847 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11848 }
11849
11850 /* create another bandwidth control object that will be mutable */
11851 unconst(mBandwidthControl).createObject();
11852 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11853
11854 /* default is to delete saved state on Saved -> PoweredOff transition */
11855 mRemoveSavedState = true;
11856
11857 /* Confirm a successful initialization when it's the case */
11858 autoInitSpan.setSucceeded();
11859
11860 LogFlowThisFuncLeave();
11861 return S_OK;
11862}
11863
11864/**
11865 * Uninitializes this session object. If the reason is other than
11866 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11867 *
11868 * @param aReason uninitialization reason
11869 *
11870 * @note Locks mParent + this object for writing.
11871 */
11872void SessionMachine::uninit(Uninit::Reason aReason)
11873{
11874 LogFlowThisFuncEnter();
11875 LogFlowThisFunc(("reason=%d\n", aReason));
11876
11877 /*
11878 * Strongly reference ourselves to prevent this object deletion after
11879 * mData->mSession.mMachine.setNull() below (which can release the last
11880 * reference and call the destructor). Important: this must be done before
11881 * accessing any members (and before AutoUninitSpan that does it as well).
11882 * This self reference will be released as the very last step on return.
11883 */
11884 ComObjPtr<SessionMachine> selfRef = this;
11885
11886 /* Enclose the state transition Ready->InUninit->NotReady */
11887 AutoUninitSpan autoUninitSpan(this);
11888 if (autoUninitSpan.uninitDone())
11889 {
11890 LogFlowThisFunc(("Already uninitialized\n"));
11891 LogFlowThisFuncLeave();
11892 return;
11893 }
11894
11895 if (autoUninitSpan.initFailed())
11896 {
11897 /* We've been called by init() because it's failed. It's not really
11898 * necessary (nor it's safe) to perform the regular uninit sequence
11899 * below, the following is enough.
11900 */
11901 LogFlowThisFunc(("Initialization failed.\n"));
11902#if defined(RT_OS_WINDOWS)
11903 if (mIPCSem)
11904 ::CloseHandle(mIPCSem);
11905 mIPCSem = NULL;
11906#elif defined(RT_OS_OS2)
11907 if (mIPCSem != NULLHANDLE)
11908 ::DosCloseMutexSem(mIPCSem);
11909 mIPCSem = NULLHANDLE;
11910#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11911 if (mIPCSem >= 0)
11912 ::semctl(mIPCSem, 0, IPC_RMID);
11913 mIPCSem = -1;
11914# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11915 mIPCKey = "0";
11916# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11917#else
11918# error "Port me!"
11919#endif
11920 uninitDataAndChildObjects();
11921 mData.free();
11922 unconst(mParent) = NULL;
11923 unconst(mPeer) = NULL;
11924 LogFlowThisFuncLeave();
11925 return;
11926 }
11927
11928 MachineState_T lastState;
11929 {
11930 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11931 lastState = mData->mMachineState;
11932 }
11933 NOREF(lastState);
11934
11935#ifdef VBOX_WITH_USB
11936 // release all captured USB devices, but do this before requesting the locks below
11937 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11938 {
11939 /* Console::captureUSBDevices() is called in the VM process only after
11940 * setting the machine state to Starting or Restoring.
11941 * Console::detachAllUSBDevices() will be called upon successful
11942 * termination. So, we need to release USB devices only if there was
11943 * an abnormal termination of a running VM.
11944 *
11945 * This is identical to SessionMachine::DetachAllUSBDevices except
11946 * for the aAbnormal argument. */
11947 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11948 AssertComRC(rc);
11949 NOREF(rc);
11950
11951 USBProxyService *service = mParent->host()->usbProxyService();
11952 if (service)
11953 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11954 }
11955#endif /* VBOX_WITH_USB */
11956
11957 // we need to lock this object in uninit() because the lock is shared
11958 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11959 // and others need mParent lock, and USB needs host lock.
11960 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11961
11962#if 0
11963 // Trigger async cleanup tasks, avoid doing things here which are not
11964 // vital to be done immediately and maybe need more locks. This calls
11965 // Machine::unregisterMetrics().
11966 mParent->onMachineUninit(mPeer);
11967#else
11968 /*
11969 * It is safe to call Machine::unregisterMetrics() here because
11970 * PerformanceCollector::samplerCallback no longer accesses guest methods
11971 * holding the lock.
11972 */
11973 unregisterMetrics(mParent->performanceCollector(), mPeer);
11974#endif
11975 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11976 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11977 this, __PRETTY_FUNCTION__, mCollectorGuest));
11978 if (mCollectorGuest)
11979 {
11980 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11981 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11982 mCollectorGuest = NULL;
11983 }
11984
11985 if (aReason == Uninit::Abnormal)
11986 {
11987 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11988 Global::IsOnlineOrTransient(lastState)));
11989
11990 /* reset the state to Aborted */
11991 if (mData->mMachineState != MachineState_Aborted)
11992 setMachineState(MachineState_Aborted);
11993 }
11994
11995 // any machine settings modified?
11996 if (mData->flModifications)
11997 {
11998 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11999 rollback(false /* aNotify */);
12000 }
12001
12002 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12003 || !mConsoleTaskData.mSnapshot);
12004 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12005 {
12006 LogWarningThisFunc(("canceling failed save state request!\n"));
12007 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12008 }
12009 else if (!mConsoleTaskData.mSnapshot.isNull())
12010 {
12011 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12012
12013 /* delete all differencing hard disks created (this will also attach
12014 * their parents back by rolling back mMediaData) */
12015 rollbackMedia();
12016
12017 // delete the saved state file (it might have been already created)
12018 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12019 // think it's still in use
12020 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12021 mConsoleTaskData.mSnapshot->uninit();
12022 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12023 }
12024
12025 if (!mData->mSession.mType.isEmpty())
12026 {
12027 /* mType is not null when this machine's process has been started by
12028 * Machine::LaunchVMProcess(), therefore it is our child. We
12029 * need to queue the PID to reap the process (and avoid zombies on
12030 * Linux). */
12031 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12032 mParent->addProcessToReap(mData->mSession.mPID);
12033 }
12034
12035 mData->mSession.mPID = NIL_RTPROCESS;
12036
12037 if (aReason == Uninit::Unexpected)
12038 {
12039 /* Uninitialization didn't come from #checkForDeath(), so tell the
12040 * client watcher thread to update the set of machines that have open
12041 * sessions. */
12042 mParent->updateClientWatcher();
12043 }
12044
12045 /* uninitialize all remote controls */
12046 if (mData->mSession.mRemoteControls.size())
12047 {
12048 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12049 mData->mSession.mRemoteControls.size()));
12050
12051 Data::Session::RemoteControlList::iterator it =
12052 mData->mSession.mRemoteControls.begin();
12053 while (it != mData->mSession.mRemoteControls.end())
12054 {
12055 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12056 HRESULT rc = (*it)->Uninitialize();
12057 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12058 if (FAILED(rc))
12059 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12060 ++it;
12061 }
12062 mData->mSession.mRemoteControls.clear();
12063 }
12064
12065 /*
12066 * An expected uninitialization can come only from #checkForDeath().
12067 * Otherwise it means that something's gone really wrong (for example,
12068 * the Session implementation has released the VirtualBox reference
12069 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12070 * etc). However, it's also possible, that the client releases the IPC
12071 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12072 * but the VirtualBox release event comes first to the server process.
12073 * This case is practically possible, so we should not assert on an
12074 * unexpected uninit, just log a warning.
12075 */
12076
12077 if ((aReason == Uninit::Unexpected))
12078 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12079
12080 if (aReason != Uninit::Normal)
12081 {
12082 mData->mSession.mDirectControl.setNull();
12083 }
12084 else
12085 {
12086 /* this must be null here (see #OnSessionEnd()) */
12087 Assert(mData->mSession.mDirectControl.isNull());
12088 Assert(mData->mSession.mState == SessionState_Unlocking);
12089 Assert(!mData->mSession.mProgress.isNull());
12090 }
12091 if (mData->mSession.mProgress)
12092 {
12093 if (aReason == Uninit::Normal)
12094 mData->mSession.mProgress->notifyComplete(S_OK);
12095 else
12096 mData->mSession.mProgress->notifyComplete(E_FAIL,
12097 COM_IIDOF(ISession),
12098 getComponentName(),
12099 tr("The VM session was aborted"));
12100 mData->mSession.mProgress.setNull();
12101 }
12102
12103 /* remove the association between the peer machine and this session machine */
12104 Assert( (SessionMachine*)mData->mSession.mMachine == this
12105 || aReason == Uninit::Unexpected);
12106
12107 /* reset the rest of session data */
12108 mData->mSession.mMachine.setNull();
12109 mData->mSession.mState = SessionState_Unlocked;
12110 mData->mSession.mType.setNull();
12111
12112 /* close the interprocess semaphore before leaving the exclusive lock */
12113#if defined(RT_OS_WINDOWS)
12114 if (mIPCSem)
12115 ::CloseHandle(mIPCSem);
12116 mIPCSem = NULL;
12117#elif defined(RT_OS_OS2)
12118 if (mIPCSem != NULLHANDLE)
12119 ::DosCloseMutexSem(mIPCSem);
12120 mIPCSem = NULLHANDLE;
12121#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12122 if (mIPCSem >= 0)
12123 ::semctl(mIPCSem, 0, IPC_RMID);
12124 mIPCSem = -1;
12125# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12126 mIPCKey = "0";
12127# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12128#else
12129# error "Port me!"
12130#endif
12131
12132 /* fire an event */
12133 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12134
12135 uninitDataAndChildObjects();
12136
12137 /* free the essential data structure last */
12138 mData.free();
12139
12140 /* release the exclusive lock before setting the below two to NULL */
12141 multilock.release();
12142
12143 unconst(mParent) = NULL;
12144 unconst(mPeer) = NULL;
12145
12146 LogFlowThisFuncLeave();
12147}
12148
12149// util::Lockable interface
12150////////////////////////////////////////////////////////////////////////////////
12151
12152/**
12153 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12154 * with the primary Machine instance (mPeer).
12155 */
12156RWLockHandle *SessionMachine::lockHandle() const
12157{
12158 AssertReturn(mPeer != NULL, NULL);
12159 return mPeer->lockHandle();
12160}
12161
12162// IInternalMachineControl methods
12163////////////////////////////////////////////////////////////////////////////////
12164
12165/**
12166 * Passes collected guest statistics to performance collector object
12167 */
12168STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12169 ULONG aCpuKernel, ULONG aCpuIdle,
12170 ULONG aMemTotal, ULONG aMemFree,
12171 ULONG aMemBalloon, ULONG aMemShared,
12172 ULONG aMemCache, ULONG aPageTotal,
12173 ULONG aAllocVMM, ULONG aFreeVMM,
12174 ULONG aBalloonedVMM, ULONG aSharedVMM,
12175 ULONG aVmNetRx, ULONG aVmNetTx)
12176{
12177 if (mCollectorGuest)
12178 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12179 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12180 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12181 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12182
12183 return S_OK;
12184}
12185
12186/**
12187 * @note Locks this object for writing.
12188 */
12189STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12190{
12191 AutoCaller autoCaller(this);
12192 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12193
12194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12195
12196 mRemoveSavedState = aRemove;
12197
12198 return S_OK;
12199}
12200
12201/**
12202 * @note Locks the same as #setMachineState() does.
12203 */
12204STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12205{
12206 return setMachineState(aMachineState);
12207}
12208
12209/**
12210 * @note Locks this object for reading.
12211 */
12212STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12213{
12214 AutoCaller autoCaller(this);
12215 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12216
12217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12218
12219#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12220 mIPCSemName.cloneTo(aId);
12221 return S_OK;
12222#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12223# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12224 mIPCKey.cloneTo(aId);
12225# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12226 mData->m_strConfigFileFull.cloneTo(aId);
12227# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12228 return S_OK;
12229#else
12230# error "Port me!"
12231#endif
12232}
12233
12234/**
12235 * @note Locks this object for writing.
12236 */
12237STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12238{
12239 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12240 AutoCaller autoCaller(this);
12241 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12242
12243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12244
12245 if (mData->mSession.mState != SessionState_Locked)
12246 return VBOX_E_INVALID_OBJECT_STATE;
12247
12248 if (!mData->mSession.mProgress.isNull())
12249 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12250
12251 LogFlowThisFunc(("returns S_OK.\n"));
12252 return S_OK;
12253}
12254
12255/**
12256 * @note Locks this object for writing.
12257 */
12258STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12259{
12260 AutoCaller autoCaller(this);
12261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12262
12263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12264
12265 if (mData->mSession.mState != SessionState_Locked)
12266 return VBOX_E_INVALID_OBJECT_STATE;
12267
12268 /* Finalize the LaunchVMProcess progress object. */
12269 if (mData->mSession.mProgress)
12270 {
12271 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12272 mData->mSession.mProgress.setNull();
12273 }
12274
12275 if (SUCCEEDED((HRESULT)iResult))
12276 {
12277#ifdef VBOX_WITH_RESOURCE_USAGE_API
12278 /* The VM has been powered up successfully, so it makes sense
12279 * now to offer the performance metrics for a running machine
12280 * object. Doing it earlier wouldn't be safe. */
12281 registerMetrics(mParent->performanceCollector(), mPeer,
12282 mData->mSession.mPID);
12283#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12284 }
12285
12286 return S_OK;
12287}
12288
12289/**
12290 * @note Locks this object for writing.
12291 */
12292STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12293{
12294 LogFlowThisFuncEnter();
12295
12296 CheckComArgOutPointerValid(aProgress);
12297
12298 AutoCaller autoCaller(this);
12299 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12300
12301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12302
12303 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12304 E_FAIL);
12305
12306 /* create a progress object to track operation completion */
12307 ComObjPtr<Progress> pProgress;
12308 pProgress.createObject();
12309 pProgress->init(getVirtualBox(),
12310 static_cast<IMachine *>(this) /* aInitiator */,
12311 Bstr(tr("Stopping the virtual machine")).raw(),
12312 FALSE /* aCancelable */);
12313
12314 /* fill in the console task data */
12315 mConsoleTaskData.mLastState = mData->mMachineState;
12316 mConsoleTaskData.mProgress = pProgress;
12317
12318 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12319 setMachineState(MachineState_Stopping);
12320
12321 pProgress.queryInterfaceTo(aProgress);
12322
12323 return S_OK;
12324}
12325
12326/**
12327 * @note Locks this object for writing.
12328 */
12329STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12330{
12331 LogFlowThisFuncEnter();
12332
12333 AutoCaller autoCaller(this);
12334 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12335
12336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12337
12338 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12339 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12340 && mConsoleTaskData.mLastState != MachineState_Null,
12341 E_FAIL);
12342
12343 /*
12344 * On failure, set the state to the state we had when BeginPoweringDown()
12345 * was called (this is expected by Console::PowerDown() and the associated
12346 * task). On success the VM process already changed the state to
12347 * MachineState_PoweredOff, so no need to do anything.
12348 */
12349 if (FAILED(iResult))
12350 setMachineState(mConsoleTaskData.mLastState);
12351
12352 /* notify the progress object about operation completion */
12353 Assert(mConsoleTaskData.mProgress);
12354 if (SUCCEEDED(iResult))
12355 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12356 else
12357 {
12358 Utf8Str strErrMsg(aErrMsg);
12359 if (strErrMsg.length())
12360 mConsoleTaskData.mProgress->notifyComplete(iResult,
12361 COM_IIDOF(ISession),
12362 getComponentName(),
12363 strErrMsg.c_str());
12364 else
12365 mConsoleTaskData.mProgress->notifyComplete(iResult);
12366 }
12367
12368 /* clear out the temporary saved state data */
12369 mConsoleTaskData.mLastState = MachineState_Null;
12370 mConsoleTaskData.mProgress.setNull();
12371
12372 LogFlowThisFuncLeave();
12373 return S_OK;
12374}
12375
12376
12377/**
12378 * Goes through the USB filters of the given machine to see if the given
12379 * device matches any filter or not.
12380 *
12381 * @note Locks the same as USBController::hasMatchingFilter() does.
12382 */
12383STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12384 BOOL *aMatched,
12385 ULONG *aMaskedIfs)
12386{
12387 LogFlowThisFunc(("\n"));
12388
12389 CheckComArgNotNull(aUSBDevice);
12390 CheckComArgOutPointerValid(aMatched);
12391
12392 AutoCaller autoCaller(this);
12393 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12394
12395#ifdef VBOX_WITH_USB
12396 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12397#else
12398 NOREF(aUSBDevice);
12399 NOREF(aMaskedIfs);
12400 *aMatched = FALSE;
12401#endif
12402
12403 return S_OK;
12404}
12405
12406/**
12407 * @note Locks the same as Host::captureUSBDevice() does.
12408 */
12409STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12410{
12411 LogFlowThisFunc(("\n"));
12412
12413 AutoCaller autoCaller(this);
12414 AssertComRCReturnRC(autoCaller.rc());
12415
12416#ifdef VBOX_WITH_USB
12417 /* if captureDeviceForVM() fails, it must have set extended error info */
12418 clearError();
12419 MultiResult rc = mParent->host()->checkUSBProxyService();
12420 if (FAILED(rc)) return rc;
12421
12422 USBProxyService *service = mParent->host()->usbProxyService();
12423 AssertReturn(service, E_FAIL);
12424 return service->captureDeviceForVM(this, Guid(aId).ref());
12425#else
12426 NOREF(aId);
12427 return E_NOTIMPL;
12428#endif
12429}
12430
12431/**
12432 * @note Locks the same as Host::detachUSBDevice() does.
12433 */
12434STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12435{
12436 LogFlowThisFunc(("\n"));
12437
12438 AutoCaller autoCaller(this);
12439 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12440
12441#ifdef VBOX_WITH_USB
12442 USBProxyService *service = mParent->host()->usbProxyService();
12443 AssertReturn(service, E_FAIL);
12444 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12445#else
12446 NOREF(aId);
12447 NOREF(aDone);
12448 return E_NOTIMPL;
12449#endif
12450}
12451
12452/**
12453 * Inserts all machine filters to the USB proxy service and then calls
12454 * Host::autoCaptureUSBDevices().
12455 *
12456 * Called by Console from the VM process upon VM startup.
12457 *
12458 * @note Locks what called methods lock.
12459 */
12460STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12461{
12462 LogFlowThisFunc(("\n"));
12463
12464 AutoCaller autoCaller(this);
12465 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12466
12467#ifdef VBOX_WITH_USB
12468 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12469 AssertComRC(rc);
12470 NOREF(rc);
12471
12472 USBProxyService *service = mParent->host()->usbProxyService();
12473 AssertReturn(service, E_FAIL);
12474 return service->autoCaptureDevicesForVM(this);
12475#else
12476 return S_OK;
12477#endif
12478}
12479
12480/**
12481 * Removes all machine filters from the USB proxy service and then calls
12482 * Host::detachAllUSBDevices().
12483 *
12484 * Called by Console from the VM process upon normal VM termination or by
12485 * SessionMachine::uninit() upon abnormal VM termination (from under the
12486 * Machine/SessionMachine lock).
12487 *
12488 * @note Locks what called methods lock.
12489 */
12490STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12491{
12492 LogFlowThisFunc(("\n"));
12493
12494 AutoCaller autoCaller(this);
12495 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12496
12497#ifdef VBOX_WITH_USB
12498 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12499 AssertComRC(rc);
12500 NOREF(rc);
12501
12502 USBProxyService *service = mParent->host()->usbProxyService();
12503 AssertReturn(service, E_FAIL);
12504 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12505#else
12506 NOREF(aDone);
12507 return S_OK;
12508#endif
12509}
12510
12511/**
12512 * @note Locks this object for writing.
12513 */
12514STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12515 IProgress **aProgress)
12516{
12517 LogFlowThisFuncEnter();
12518
12519 AssertReturn(aSession, E_INVALIDARG);
12520 AssertReturn(aProgress, E_INVALIDARG);
12521
12522 AutoCaller autoCaller(this);
12523
12524 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12525 /*
12526 * We don't assert below because it might happen that a non-direct session
12527 * informs us it is closed right after we've been uninitialized -- it's ok.
12528 */
12529 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12530
12531 /* get IInternalSessionControl interface */
12532 ComPtr<IInternalSessionControl> control(aSession);
12533
12534 ComAssertRet(!control.isNull(), E_INVALIDARG);
12535
12536 /* Creating a Progress object requires the VirtualBox lock, and
12537 * thus locking it here is required by the lock order rules. */
12538 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12539
12540 if (control == mData->mSession.mDirectControl)
12541 {
12542 ComAssertRet(aProgress, E_POINTER);
12543
12544 /* The direct session is being normally closed by the client process
12545 * ----------------------------------------------------------------- */
12546
12547 /* go to the closing state (essential for all open*Session() calls and
12548 * for #checkForDeath()) */
12549 Assert(mData->mSession.mState == SessionState_Locked);
12550 mData->mSession.mState = SessionState_Unlocking;
12551
12552 /* set direct control to NULL to release the remote instance */
12553 mData->mSession.mDirectControl.setNull();
12554 LogFlowThisFunc(("Direct control is set to NULL\n"));
12555
12556 if (mData->mSession.mProgress)
12557 {
12558 /* finalize the progress, someone might wait if a frontend
12559 * closes the session before powering on the VM. */
12560 mData->mSession.mProgress->notifyComplete(E_FAIL,
12561 COM_IIDOF(ISession),
12562 getComponentName(),
12563 tr("The VM session was closed before any attempt to power it on"));
12564 mData->mSession.mProgress.setNull();
12565 }
12566
12567 /* Create the progress object the client will use to wait until
12568 * #checkForDeath() is called to uninitialize this session object after
12569 * it releases the IPC semaphore.
12570 * Note! Because we're "reusing" mProgress here, this must be a proxy
12571 * object just like for LaunchVMProcess. */
12572 Assert(mData->mSession.mProgress.isNull());
12573 ComObjPtr<ProgressProxy> progress;
12574 progress.createObject();
12575 ComPtr<IUnknown> pPeer(mPeer);
12576 progress->init(mParent, pPeer,
12577 Bstr(tr("Closing session")).raw(),
12578 FALSE /* aCancelable */);
12579 progress.queryInterfaceTo(aProgress);
12580 mData->mSession.mProgress = progress;
12581 }
12582 else
12583 {
12584 /* the remote session is being normally closed */
12585 Data::Session::RemoteControlList::iterator it =
12586 mData->mSession.mRemoteControls.begin();
12587 while (it != mData->mSession.mRemoteControls.end())
12588 {
12589 if (control == *it)
12590 break;
12591 ++it;
12592 }
12593 BOOL found = it != mData->mSession.mRemoteControls.end();
12594 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12595 E_INVALIDARG);
12596 // This MUST be erase(it), not remove(*it) as the latter triggers a
12597 // very nasty use after free due to the place where the value "lives".
12598 mData->mSession.mRemoteControls.erase(it);
12599 }
12600
12601 /* signal the client watcher thread, because the client is going away */
12602 mParent->updateClientWatcher();
12603
12604 LogFlowThisFuncLeave();
12605 return S_OK;
12606}
12607
12608/**
12609 * @note Locks this object for writing.
12610 */
12611STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12612{
12613 LogFlowThisFuncEnter();
12614
12615 CheckComArgOutPointerValid(aProgress);
12616 CheckComArgOutPointerValid(aStateFilePath);
12617
12618 AutoCaller autoCaller(this);
12619 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12620
12621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12622
12623 AssertReturn( mData->mMachineState == MachineState_Paused
12624 && mConsoleTaskData.mLastState == MachineState_Null
12625 && mConsoleTaskData.strStateFilePath.isEmpty(),
12626 E_FAIL);
12627
12628 /* create a progress object to track operation completion */
12629 ComObjPtr<Progress> pProgress;
12630 pProgress.createObject();
12631 pProgress->init(getVirtualBox(),
12632 static_cast<IMachine *>(this) /* aInitiator */,
12633 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12634 FALSE /* aCancelable */);
12635
12636 Utf8Str strStateFilePath;
12637 /* stateFilePath is null when the machine is not running */
12638 if (mData->mMachineState == MachineState_Paused)
12639 composeSavedStateFilename(strStateFilePath);
12640
12641 /* fill in the console task data */
12642 mConsoleTaskData.mLastState = mData->mMachineState;
12643 mConsoleTaskData.strStateFilePath = strStateFilePath;
12644 mConsoleTaskData.mProgress = pProgress;
12645
12646 /* set the state to Saving (this is expected by Console::SaveState()) */
12647 setMachineState(MachineState_Saving);
12648
12649 strStateFilePath.cloneTo(aStateFilePath);
12650 pProgress.queryInterfaceTo(aProgress);
12651
12652 return S_OK;
12653}
12654
12655/**
12656 * @note Locks mParent + this object for writing.
12657 */
12658STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12659{
12660 LogFlowThisFunc(("\n"));
12661
12662 AutoCaller autoCaller(this);
12663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12664
12665 /* endSavingState() need mParent lock */
12666 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12667
12668 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12669 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12670 && mConsoleTaskData.mLastState != MachineState_Null
12671 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12672 E_FAIL);
12673
12674 /*
12675 * On failure, set the state to the state we had when BeginSavingState()
12676 * was called (this is expected by Console::SaveState() and the associated
12677 * task). On success the VM process already changed the state to
12678 * MachineState_Saved, so no need to do anything.
12679 */
12680 if (FAILED(iResult))
12681 setMachineState(mConsoleTaskData.mLastState);
12682
12683 return endSavingState(iResult, aErrMsg);
12684}
12685
12686/**
12687 * @note Locks this object for writing.
12688 */
12689STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12690{
12691 LogFlowThisFunc(("\n"));
12692
12693 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12694
12695 AutoCaller autoCaller(this);
12696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12697
12698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12699
12700 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12701 || mData->mMachineState == MachineState_Teleported
12702 || mData->mMachineState == MachineState_Aborted
12703 , E_FAIL); /** @todo setError. */
12704
12705 Utf8Str stateFilePathFull = aSavedStateFile;
12706 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12707 if (RT_FAILURE(vrc))
12708 return setError(VBOX_E_FILE_ERROR,
12709 tr("Invalid saved state file path '%ls' (%Rrc)"),
12710 aSavedStateFile,
12711 vrc);
12712
12713 mSSData->strStateFilePath = stateFilePathFull;
12714
12715 /* The below setMachineState() will detect the state transition and will
12716 * update the settings file */
12717
12718 return setMachineState(MachineState_Saved);
12719}
12720
12721STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12722 ComSafeArrayOut(BSTR, aValues),
12723 ComSafeArrayOut(LONG64, aTimestamps),
12724 ComSafeArrayOut(BSTR, aFlags))
12725{
12726 LogFlowThisFunc(("\n"));
12727
12728#ifdef VBOX_WITH_GUEST_PROPS
12729 using namespace guestProp;
12730
12731 AutoCaller autoCaller(this);
12732 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12733
12734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12735
12736 CheckComArgOutSafeArrayPointerValid(aNames);
12737 CheckComArgOutSafeArrayPointerValid(aValues);
12738 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12739 CheckComArgOutSafeArrayPointerValid(aFlags);
12740
12741 size_t cEntries = mHWData->mGuestProperties.size();
12742 com::SafeArray<BSTR> names(cEntries);
12743 com::SafeArray<BSTR> values(cEntries);
12744 com::SafeArray<LONG64> timestamps(cEntries);
12745 com::SafeArray<BSTR> flags(cEntries);
12746 unsigned i = 0;
12747 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12748 it != mHWData->mGuestProperties.end();
12749 ++it)
12750 {
12751 char szFlags[MAX_FLAGS_LEN + 1];
12752 it->strName.cloneTo(&names[i]);
12753 it->strValue.cloneTo(&values[i]);
12754 timestamps[i] = it->mTimestamp;
12755 /* If it is NULL, keep it NULL. */
12756 if (it->mFlags)
12757 {
12758 writeFlags(it->mFlags, szFlags);
12759 Bstr(szFlags).cloneTo(&flags[i]);
12760 }
12761 else
12762 flags[i] = NULL;
12763 ++i;
12764 }
12765 names.detachTo(ComSafeArrayOutArg(aNames));
12766 values.detachTo(ComSafeArrayOutArg(aValues));
12767 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12768 flags.detachTo(ComSafeArrayOutArg(aFlags));
12769 return S_OK;
12770#else
12771 ReturnComNotImplemented();
12772#endif
12773}
12774
12775STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12776 IN_BSTR aValue,
12777 LONG64 aTimestamp,
12778 IN_BSTR aFlags)
12779{
12780 LogFlowThisFunc(("\n"));
12781
12782#ifdef VBOX_WITH_GUEST_PROPS
12783 using namespace guestProp;
12784
12785 CheckComArgStrNotEmptyOrNull(aName);
12786 CheckComArgNotNull(aValue);
12787 CheckComArgNotNull(aFlags);
12788
12789 try
12790 {
12791 /*
12792 * Convert input up front.
12793 */
12794 Utf8Str utf8Name(aName);
12795 uint32_t fFlags = NILFLAG;
12796 if (aFlags)
12797 {
12798 Utf8Str utf8Flags(aFlags);
12799 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12800 AssertRCReturn(vrc, E_INVALIDARG);
12801 }
12802
12803 /*
12804 * Now grab the object lock, validate the state and do the update.
12805 */
12806 AutoCaller autoCaller(this);
12807 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12808
12809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12810
12811 switch (mData->mMachineState)
12812 {
12813 case MachineState_Paused:
12814 case MachineState_Running:
12815 case MachineState_Teleporting:
12816 case MachineState_TeleportingPausedVM:
12817 case MachineState_LiveSnapshotting:
12818 case MachineState_DeletingSnapshotOnline:
12819 case MachineState_DeletingSnapshotPaused:
12820 case MachineState_Saving:
12821 break;
12822
12823 default:
12824#ifndef DEBUG_sunlover
12825 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12826 VBOX_E_INVALID_VM_STATE);
12827#else
12828 return VBOX_E_INVALID_VM_STATE;
12829#endif
12830 }
12831
12832 setModified(IsModified_MachineData);
12833 mHWData.backup();
12834
12835 /** @todo r=bird: The careful memory handling doesn't work out here because
12836 * the catch block won't undo any damage we've done. So, if push_back throws
12837 * bad_alloc then you've lost the value.
12838 *
12839 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12840 * since values that changes actually bubbles to the end of the list. Using
12841 * something that has an efficient lookup and can tolerate a bit of updates
12842 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12843 * combination of RTStrCache (for sharing names and getting uniqueness into
12844 * the bargain) and hash/tree is another. */
12845 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12846 iter != mHWData->mGuestProperties.end();
12847 ++iter)
12848 if (utf8Name == iter->strName)
12849 {
12850 mHWData->mGuestProperties.erase(iter);
12851 mData->mGuestPropertiesModified = TRUE;
12852 break;
12853 }
12854 if (aValue != NULL)
12855 {
12856 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12857 mHWData->mGuestProperties.push_back(property);
12858 mData->mGuestPropertiesModified = TRUE;
12859 }
12860
12861 /*
12862 * Send a callback notification if appropriate
12863 */
12864 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12865 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12866 RTSTR_MAX,
12867 utf8Name.c_str(),
12868 RTSTR_MAX, NULL)
12869 )
12870 {
12871 alock.release();
12872
12873 mParent->onGuestPropertyChange(mData->mUuid,
12874 aName,
12875 aValue,
12876 aFlags);
12877 }
12878 }
12879 catch (...)
12880 {
12881 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12882 }
12883 return S_OK;
12884#else
12885 ReturnComNotImplemented();
12886#endif
12887}
12888
12889STDMETHODIMP SessionMachine::LockMedia()
12890{
12891 AutoCaller autoCaller(this);
12892 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12893
12894 AutoMultiWriteLock2 alock(this->lockHandle(),
12895 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12896
12897 AssertReturn( mData->mMachineState == MachineState_Starting
12898 || mData->mMachineState == MachineState_Restoring
12899 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
12900
12901 clearError();
12902 alock.release();
12903 return lockMedia();
12904}
12905
12906STDMETHODIMP SessionMachine::UnlockMedia()
12907{
12908 unlockMedia();
12909 return S_OK;
12910}
12911
12912STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12913 IMediumAttachment **aNewAttachment)
12914{
12915 CheckComArgNotNull(aAttachment);
12916 CheckComArgOutPointerValid(aNewAttachment);
12917
12918 AutoCaller autoCaller(this);
12919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12920
12921 // request the host lock first, since might be calling Host methods for getting host drives;
12922 // next, protect the media tree all the while we're in here, as well as our member variables
12923 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12924 this->lockHandle(),
12925 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12926
12927 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12928
12929 Bstr ctrlName;
12930 LONG lPort;
12931 LONG lDevice;
12932 bool fTempEject;
12933 {
12934 AutoCaller autoAttachCaller(this);
12935 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12936
12937 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12938
12939 /* Need to query the details first, as the IMediumAttachment reference
12940 * might be to the original settings, which we are going to change. */
12941 ctrlName = pAttach->getControllerName();
12942 lPort = pAttach->getPort();
12943 lDevice = pAttach->getDevice();
12944 fTempEject = pAttach->getTempEject();
12945 }
12946
12947 if (!fTempEject)
12948 {
12949 /* Remember previously mounted medium. The medium before taking the
12950 * backup is not necessarily the same thing. */
12951 ComObjPtr<Medium> oldmedium;
12952 oldmedium = pAttach->getMedium();
12953
12954 setModified(IsModified_Storage);
12955 mMediaData.backup();
12956
12957 // The backup operation makes the pAttach reference point to the
12958 // old settings. Re-get the correct reference.
12959 pAttach = findAttachment(mMediaData->mAttachments,
12960 ctrlName.raw(),
12961 lPort,
12962 lDevice);
12963
12964 {
12965 AutoCaller autoAttachCaller(this);
12966 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12967
12968 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12969 if (!oldmedium.isNull())
12970 oldmedium->removeBackReference(mData->mUuid);
12971
12972 pAttach->updateMedium(NULL);
12973 pAttach->updateEjected();
12974 }
12975
12976 setModified(IsModified_Storage);
12977 }
12978 else
12979 {
12980 {
12981 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12982 pAttach->updateEjected();
12983 }
12984 }
12985
12986 pAttach.queryInterfaceTo(aNewAttachment);
12987
12988 return S_OK;
12989}
12990
12991// public methods only for internal purposes
12992/////////////////////////////////////////////////////////////////////////////
12993
12994/**
12995 * Called from the client watcher thread to check for expected or unexpected
12996 * death of the client process that has a direct session to this machine.
12997 *
12998 * On Win32 and on OS/2, this method is called only when we've got the
12999 * mutex (i.e. the client has either died or terminated normally) so it always
13000 * returns @c true (the client is terminated, the session machine is
13001 * uninitialized).
13002 *
13003 * On other platforms, the method returns @c true if the client process has
13004 * terminated normally or abnormally and the session machine was uninitialized,
13005 * and @c false if the client process is still alive.
13006 *
13007 * @note Locks this object for writing.
13008 */
13009bool SessionMachine::checkForDeath()
13010{
13011 Uninit::Reason reason;
13012 bool terminated = false;
13013
13014 /* Enclose autoCaller with a block because calling uninit() from under it
13015 * will deadlock. */
13016 {
13017 AutoCaller autoCaller(this);
13018 if (!autoCaller.isOk())
13019 {
13020 /* return true if not ready, to cause the client watcher to exclude
13021 * the corresponding session from watching */
13022 LogFlowThisFunc(("Already uninitialized!\n"));
13023 return true;
13024 }
13025
13026 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13027
13028 /* Determine the reason of death: if the session state is Closing here,
13029 * everything is fine. Otherwise it means that the client did not call
13030 * OnSessionEnd() before it released the IPC semaphore. This may happen
13031 * either because the client process has abnormally terminated, or
13032 * because it simply forgot to call ISession::Close() before exiting. We
13033 * threat the latter also as an abnormal termination (see
13034 * Session::uninit() for details). */
13035 reason = mData->mSession.mState == SessionState_Unlocking ?
13036 Uninit::Normal :
13037 Uninit::Abnormal;
13038
13039#if defined(RT_OS_WINDOWS)
13040
13041 AssertMsg(mIPCSem, ("semaphore must be created"));
13042
13043 /* release the IPC mutex */
13044 ::ReleaseMutex(mIPCSem);
13045
13046 terminated = true;
13047
13048#elif defined(RT_OS_OS2)
13049
13050 AssertMsg(mIPCSem, ("semaphore must be created"));
13051
13052 /* release the IPC mutex */
13053 ::DosReleaseMutexSem(mIPCSem);
13054
13055 terminated = true;
13056
13057#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13058
13059 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13060
13061 int val = ::semctl(mIPCSem, 0, GETVAL);
13062 if (val > 0)
13063 {
13064 /* the semaphore is signaled, meaning the session is terminated */
13065 terminated = true;
13066 }
13067
13068#else
13069# error "Port me!"
13070#endif
13071
13072 } /* AutoCaller block */
13073
13074 if (terminated)
13075 uninit(reason);
13076
13077 return terminated;
13078}
13079
13080/**
13081 * @note Locks this object for reading.
13082 */
13083HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13084{
13085 LogFlowThisFunc(("\n"));
13086
13087 AutoCaller autoCaller(this);
13088 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13089
13090 ComPtr<IInternalSessionControl> directControl;
13091 {
13092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13093 directControl = mData->mSession.mDirectControl;
13094 }
13095
13096 /* ignore notifications sent after #OnSessionEnd() is called */
13097 if (!directControl)
13098 return S_OK;
13099
13100 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13101}
13102
13103/**
13104 * @note Locks this object for reading.
13105 */
13106HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13107 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13108{
13109 LogFlowThisFunc(("\n"));
13110
13111 AutoCaller autoCaller(this);
13112 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13113
13114 ComPtr<IInternalSessionControl> directControl;
13115 {
13116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13117 directControl = mData->mSession.mDirectControl;
13118 }
13119
13120 /* ignore notifications sent after #OnSessionEnd() is called */
13121 if (!directControl)
13122 return S_OK;
13123 /*
13124 * instead acting like callback we ask IVirtualBox deliver corresponding event
13125 */
13126
13127 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13128 return S_OK;
13129}
13130
13131/**
13132 * @note Locks this object for reading.
13133 */
13134HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13135{
13136 LogFlowThisFunc(("\n"));
13137
13138 AutoCaller autoCaller(this);
13139 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13140
13141 ComPtr<IInternalSessionControl> directControl;
13142 {
13143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13144 directControl = mData->mSession.mDirectControl;
13145 }
13146
13147 /* ignore notifications sent after #OnSessionEnd() is called */
13148 if (!directControl)
13149 return S_OK;
13150
13151 return directControl->OnSerialPortChange(serialPort);
13152}
13153
13154/**
13155 * @note Locks this object for reading.
13156 */
13157HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13158{
13159 LogFlowThisFunc(("\n"));
13160
13161 AutoCaller autoCaller(this);
13162 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13163
13164 ComPtr<IInternalSessionControl> directControl;
13165 {
13166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13167 directControl = mData->mSession.mDirectControl;
13168 }
13169
13170 /* ignore notifications sent after #OnSessionEnd() is called */
13171 if (!directControl)
13172 return S_OK;
13173
13174 return directControl->OnParallelPortChange(parallelPort);
13175}
13176
13177/**
13178 * @note Locks this object for reading.
13179 */
13180HRESULT SessionMachine::onStorageControllerChange()
13181{
13182 LogFlowThisFunc(("\n"));
13183
13184 AutoCaller autoCaller(this);
13185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13186
13187 ComPtr<IInternalSessionControl> directControl;
13188 {
13189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13190 directControl = mData->mSession.mDirectControl;
13191 }
13192
13193 /* ignore notifications sent after #OnSessionEnd() is called */
13194 if (!directControl)
13195 return S_OK;
13196
13197 return directControl->OnStorageControllerChange();
13198}
13199
13200/**
13201 * @note Locks this object for reading.
13202 */
13203HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13204{
13205 LogFlowThisFunc(("\n"));
13206
13207 AutoCaller autoCaller(this);
13208 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13209
13210 ComPtr<IInternalSessionControl> directControl;
13211 {
13212 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13213 directControl = mData->mSession.mDirectControl;
13214 }
13215
13216 /* ignore notifications sent after #OnSessionEnd() is called */
13217 if (!directControl)
13218 return S_OK;
13219
13220 return directControl->OnMediumChange(aAttachment, aForce);
13221}
13222
13223/**
13224 * @note Locks this object for reading.
13225 */
13226HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13227{
13228 LogFlowThisFunc(("\n"));
13229
13230 AutoCaller autoCaller(this);
13231 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13232
13233 ComPtr<IInternalSessionControl> directControl;
13234 {
13235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13236 directControl = mData->mSession.mDirectControl;
13237 }
13238
13239 /* ignore notifications sent after #OnSessionEnd() is called */
13240 if (!directControl)
13241 return S_OK;
13242
13243 return directControl->OnCPUChange(aCPU, aRemove);
13244}
13245
13246HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13247{
13248 LogFlowThisFunc(("\n"));
13249
13250 AutoCaller autoCaller(this);
13251 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13252
13253 ComPtr<IInternalSessionControl> directControl;
13254 {
13255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13256 directControl = mData->mSession.mDirectControl;
13257 }
13258
13259 /* ignore notifications sent after #OnSessionEnd() is called */
13260 if (!directControl)
13261 return S_OK;
13262
13263 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13264}
13265
13266/**
13267 * @note Locks this object for reading.
13268 */
13269HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13270{
13271 LogFlowThisFunc(("\n"));
13272
13273 AutoCaller autoCaller(this);
13274 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13275
13276 ComPtr<IInternalSessionControl> directControl;
13277 {
13278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13279 directControl = mData->mSession.mDirectControl;
13280 }
13281
13282 /* ignore notifications sent after #OnSessionEnd() is called */
13283 if (!directControl)
13284 return S_OK;
13285
13286 return directControl->OnVRDEServerChange(aRestart);
13287}
13288
13289/**
13290 * @note Locks this object for reading.
13291 */
13292HRESULT SessionMachine::onUSBControllerChange()
13293{
13294 LogFlowThisFunc(("\n"));
13295
13296 AutoCaller autoCaller(this);
13297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13298
13299 ComPtr<IInternalSessionControl> directControl;
13300 {
13301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13302 directControl = mData->mSession.mDirectControl;
13303 }
13304
13305 /* ignore notifications sent after #OnSessionEnd() is called */
13306 if (!directControl)
13307 return S_OK;
13308
13309 return directControl->OnUSBControllerChange();
13310}
13311
13312/**
13313 * @note Locks this object for reading.
13314 */
13315HRESULT SessionMachine::onSharedFolderChange()
13316{
13317 LogFlowThisFunc(("\n"));
13318
13319 AutoCaller autoCaller(this);
13320 AssertComRCReturnRC(autoCaller.rc());
13321
13322 ComPtr<IInternalSessionControl> directControl;
13323 {
13324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13325 directControl = mData->mSession.mDirectControl;
13326 }
13327
13328 /* ignore notifications sent after #OnSessionEnd() is called */
13329 if (!directControl)
13330 return S_OK;
13331
13332 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13333}
13334
13335/**
13336 * @note Locks this object for reading.
13337 */
13338HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13339{
13340 LogFlowThisFunc(("\n"));
13341
13342 AutoCaller autoCaller(this);
13343 AssertComRCReturnRC(autoCaller.rc());
13344
13345 ComPtr<IInternalSessionControl> directControl;
13346 {
13347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13348 directControl = mData->mSession.mDirectControl;
13349 }
13350
13351 /* ignore notifications sent after #OnSessionEnd() is called */
13352 if (!directControl)
13353 return S_OK;
13354
13355 return directControl->OnClipboardModeChange(aClipboardMode);
13356}
13357
13358/**
13359 * @note Locks this object for reading.
13360 */
13361HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13362{
13363 LogFlowThisFunc(("\n"));
13364
13365 AutoCaller autoCaller(this);
13366 AssertComRCReturnRC(autoCaller.rc());
13367
13368 ComPtr<IInternalSessionControl> directControl;
13369 {
13370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13371 directControl = mData->mSession.mDirectControl;
13372 }
13373
13374 /* ignore notifications sent after #OnSessionEnd() is called */
13375 if (!directControl)
13376 return S_OK;
13377
13378 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13379}
13380
13381/**
13382 * @note Locks this object for reading.
13383 */
13384HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13385{
13386 LogFlowThisFunc(("\n"));
13387
13388 AutoCaller autoCaller(this);
13389 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13390
13391 ComPtr<IInternalSessionControl> directControl;
13392 {
13393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13394 directControl = mData->mSession.mDirectControl;
13395 }
13396
13397 /* ignore notifications sent after #OnSessionEnd() is called */
13398 if (!directControl)
13399 return S_OK;
13400
13401 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13402}
13403
13404/**
13405 * @note Locks this object for reading.
13406 */
13407HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13408{
13409 LogFlowThisFunc(("\n"));
13410
13411 AutoCaller autoCaller(this);
13412 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13413
13414 ComPtr<IInternalSessionControl> directControl;
13415 {
13416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13417 directControl = mData->mSession.mDirectControl;
13418 }
13419
13420 /* ignore notifications sent after #OnSessionEnd() is called */
13421 if (!directControl)
13422 return S_OK;
13423
13424 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13425}
13426
13427/**
13428 * Returns @c true if this machine's USB controller reports it has a matching
13429 * filter for the given USB device and @c false otherwise.
13430 *
13431 * @note locks this object for reading.
13432 */
13433bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13434{
13435 AutoCaller autoCaller(this);
13436 /* silently return if not ready -- this method may be called after the
13437 * direct machine session has been called */
13438 if (!autoCaller.isOk())
13439 return false;
13440
13441#ifdef VBOX_WITH_USB
13442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13443
13444 switch (mData->mMachineState)
13445 {
13446 case MachineState_Starting:
13447 case MachineState_Restoring:
13448 case MachineState_TeleportingIn:
13449 case MachineState_Paused:
13450 case MachineState_Running:
13451 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13452 * elsewhere... */
13453 alock.release();
13454 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13455 default: break;
13456 }
13457#else
13458 NOREF(aDevice);
13459 NOREF(aMaskedIfs);
13460#endif
13461 return false;
13462}
13463
13464/**
13465 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13466 */
13467HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13468 IVirtualBoxErrorInfo *aError,
13469 ULONG aMaskedIfs)
13470{
13471 LogFlowThisFunc(("\n"));
13472
13473 AutoCaller autoCaller(this);
13474
13475 /* This notification may happen after the machine object has been
13476 * uninitialized (the session was closed), so don't assert. */
13477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13478
13479 ComPtr<IInternalSessionControl> directControl;
13480 {
13481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13482 directControl = mData->mSession.mDirectControl;
13483 }
13484
13485 /* fail on notifications sent after #OnSessionEnd() is called, it is
13486 * expected by the caller */
13487 if (!directControl)
13488 return E_FAIL;
13489
13490 /* No locks should be held at this point. */
13491 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13492 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13493
13494 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13495}
13496
13497/**
13498 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13499 */
13500HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13501 IVirtualBoxErrorInfo *aError)
13502{
13503 LogFlowThisFunc(("\n"));
13504
13505 AutoCaller autoCaller(this);
13506
13507 /* This notification may happen after the machine object has been
13508 * uninitialized (the session was closed), so don't assert. */
13509 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13510
13511 ComPtr<IInternalSessionControl> directControl;
13512 {
13513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13514 directControl = mData->mSession.mDirectControl;
13515 }
13516
13517 /* fail on notifications sent after #OnSessionEnd() is called, it is
13518 * expected by the caller */
13519 if (!directControl)
13520 return E_FAIL;
13521
13522 /* No locks should be held at this point. */
13523 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13524 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13525
13526 return directControl->OnUSBDeviceDetach(aId, aError);
13527}
13528
13529// protected methods
13530/////////////////////////////////////////////////////////////////////////////
13531
13532/**
13533 * Helper method to finalize saving the state.
13534 *
13535 * @note Must be called from under this object's lock.
13536 *
13537 * @param aRc S_OK if the snapshot has been taken successfully
13538 * @param aErrMsg human readable error message for failure
13539 *
13540 * @note Locks mParent + this objects for writing.
13541 */
13542HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13543{
13544 LogFlowThisFuncEnter();
13545
13546 AutoCaller autoCaller(this);
13547 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13548
13549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13550
13551 HRESULT rc = S_OK;
13552
13553 if (SUCCEEDED(aRc))
13554 {
13555 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13556
13557 /* save all VM settings */
13558 rc = saveSettings(NULL);
13559 // no need to check whether VirtualBox.xml needs saving also since
13560 // we can't have a name change pending at this point
13561 }
13562 else
13563 {
13564 // delete the saved state file (it might have been already created);
13565 // we need not check whether this is shared with a snapshot here because
13566 // we certainly created this saved state file here anew
13567 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13568 }
13569
13570 /* notify the progress object about operation completion */
13571 Assert(mConsoleTaskData.mProgress);
13572 if (SUCCEEDED(aRc))
13573 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13574 else
13575 {
13576 if (aErrMsg.length())
13577 mConsoleTaskData.mProgress->notifyComplete(aRc,
13578 COM_IIDOF(ISession),
13579 getComponentName(),
13580 aErrMsg.c_str());
13581 else
13582 mConsoleTaskData.mProgress->notifyComplete(aRc);
13583 }
13584
13585 /* clear out the temporary saved state data */
13586 mConsoleTaskData.mLastState = MachineState_Null;
13587 mConsoleTaskData.strStateFilePath.setNull();
13588 mConsoleTaskData.mProgress.setNull();
13589
13590 LogFlowThisFuncLeave();
13591 return rc;
13592}
13593
13594/**
13595 * Deletes the given file if it is no longer in use by either the current machine state
13596 * (if the machine is "saved") or any of the machine's snapshots.
13597 *
13598 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13599 * but is different for each SnapshotMachine. When calling this, the order of calling this
13600 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13601 * is therefore critical. I know, it's all rather messy.
13602 *
13603 * @param strStateFile
13604 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13605 */
13606void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13607 Snapshot *pSnapshotToIgnore)
13608{
13609 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13610 if ( (strStateFile.isNotEmpty())
13611 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13612 )
13613 // ... and it must also not be shared with other snapshots
13614 if ( !mData->mFirstSnapshot
13615 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13616 // this checks the SnapshotMachine's state file paths
13617 )
13618 RTFileDelete(strStateFile.c_str());
13619}
13620
13621/**
13622 * Locks the attached media.
13623 *
13624 * All attached hard disks are locked for writing and DVD/floppy are locked for
13625 * reading. Parents of attached hard disks (if any) are locked for reading.
13626 *
13627 * This method also performs accessibility check of all media it locks: if some
13628 * media is inaccessible, the method will return a failure and a bunch of
13629 * extended error info objects per each inaccessible medium.
13630 *
13631 * Note that this method is atomic: if it returns a success, all media are
13632 * locked as described above; on failure no media is locked at all (all
13633 * succeeded individual locks will be undone).
13634 *
13635 * The caller is responsible for doing the necessary state sanity checks.
13636 *
13637 * The locks made by this method must be undone by calling #unlockMedia() when
13638 * no more needed.
13639 */
13640HRESULT SessionMachine::lockMedia()
13641{
13642 AutoCaller autoCaller(this);
13643 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13644
13645 AutoMultiWriteLock2 alock(this->lockHandle(),
13646 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13647
13648 /* bail out if trying to lock things with already set up locking */
13649 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13650
13651 MultiResult mrc(S_OK);
13652
13653 /* Collect locking information for all medium objects attached to the VM. */
13654 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13655 it != mMediaData->mAttachments.end();
13656 ++it)
13657 {
13658 MediumAttachment* pAtt = *it;
13659 DeviceType_T devType = pAtt->getType();
13660 Medium *pMedium = pAtt->getMedium();
13661
13662 MediumLockList *pMediumLockList(new MediumLockList());
13663 // There can be attachments without a medium (floppy/dvd), and thus
13664 // it's impossible to create a medium lock list. It still makes sense
13665 // to have the empty medium lock list in the map in case a medium is
13666 // attached later.
13667 if (pMedium != NULL)
13668 {
13669 MediumType_T mediumType = pMedium->getType();
13670 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13671 || mediumType == MediumType_Shareable;
13672 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13673
13674 alock.release();
13675 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13676 !fIsReadOnlyLock /* fMediumLockWrite */,
13677 NULL,
13678 *pMediumLockList);
13679 alock.acquire();
13680 if (FAILED(mrc))
13681 {
13682 delete pMediumLockList;
13683 mData->mSession.mLockedMedia.Clear();
13684 break;
13685 }
13686 }
13687
13688 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13689 if (FAILED(rc))
13690 {
13691 mData->mSession.mLockedMedia.Clear();
13692 mrc = setError(rc,
13693 tr("Collecting locking information for all attached media failed"));
13694 break;
13695 }
13696 }
13697
13698 if (SUCCEEDED(mrc))
13699 {
13700 /* Now lock all media. If this fails, nothing is locked. */
13701 alock.release();
13702 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13703 alock.acquire();
13704 if (FAILED(rc))
13705 {
13706 mrc = setError(rc,
13707 tr("Locking of attached media failed"));
13708 }
13709 }
13710
13711 return mrc;
13712}
13713
13714/**
13715 * Undoes the locks made by by #lockMedia().
13716 */
13717void SessionMachine::unlockMedia()
13718{
13719 AutoCaller autoCaller(this);
13720 AssertComRCReturnVoid(autoCaller.rc());
13721
13722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13723
13724 /* we may be holding important error info on the current thread;
13725 * preserve it */
13726 ErrorInfoKeeper eik;
13727
13728 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13729 AssertComRC(rc);
13730}
13731
13732/**
13733 * Helper to change the machine state (reimplementation).
13734 *
13735 * @note Locks this object for writing.
13736 * @note This method must not call saveSettings or SaveSettings, otherwise
13737 * it can cause crashes in random places due to unexpectedly committing
13738 * the current settings. The caller is responsible for that. The call
13739 * to saveStateSettings is fine, because this method does not commit.
13740 */
13741HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13742{
13743 LogFlowThisFuncEnter();
13744 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13745
13746 AutoCaller autoCaller(this);
13747 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13748
13749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13750
13751 MachineState_T oldMachineState = mData->mMachineState;
13752
13753 AssertMsgReturn(oldMachineState != aMachineState,
13754 ("oldMachineState=%s, aMachineState=%s\n",
13755 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13756 E_FAIL);
13757
13758 HRESULT rc = S_OK;
13759
13760 int stsFlags = 0;
13761 bool deleteSavedState = false;
13762
13763 /* detect some state transitions */
13764
13765 if ( ( oldMachineState == MachineState_Saved
13766 && aMachineState == MachineState_Restoring)
13767 || ( ( oldMachineState == MachineState_PoweredOff
13768 || oldMachineState == MachineState_Teleported
13769 || oldMachineState == MachineState_Aborted
13770 )
13771 && ( aMachineState == MachineState_TeleportingIn
13772 || aMachineState == MachineState_Starting
13773 )
13774 )
13775 )
13776 {
13777 /* The EMT thread is about to start */
13778
13779 /* Nothing to do here for now... */
13780
13781 /// @todo NEWMEDIA don't let mDVDDrive and other children
13782 /// change anything when in the Starting/Restoring state
13783 }
13784 else if ( ( oldMachineState == MachineState_Running
13785 || oldMachineState == MachineState_Paused
13786 || oldMachineState == MachineState_Teleporting
13787 || oldMachineState == MachineState_LiveSnapshotting
13788 || oldMachineState == MachineState_Stuck
13789 || oldMachineState == MachineState_Starting
13790 || oldMachineState == MachineState_Stopping
13791 || oldMachineState == MachineState_Saving
13792 || oldMachineState == MachineState_Restoring
13793 || oldMachineState == MachineState_TeleportingPausedVM
13794 || oldMachineState == MachineState_TeleportingIn
13795 )
13796 && ( aMachineState == MachineState_PoweredOff
13797 || aMachineState == MachineState_Saved
13798 || aMachineState == MachineState_Teleported
13799 || aMachineState == MachineState_Aborted
13800 )
13801 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13802 * snapshot */
13803 && ( mConsoleTaskData.mSnapshot.isNull()
13804 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13805 )
13806 )
13807 {
13808 /* The EMT thread has just stopped, unlock attached media. Note that as
13809 * opposed to locking that is done from Console, we do unlocking here
13810 * because the VM process may have aborted before having a chance to
13811 * properly unlock all media it locked. */
13812
13813 unlockMedia();
13814 }
13815
13816 if (oldMachineState == MachineState_Restoring)
13817 {
13818 if (aMachineState != MachineState_Saved)
13819 {
13820 /*
13821 * delete the saved state file once the machine has finished
13822 * restoring from it (note that Console sets the state from
13823 * Restoring to Saved if the VM couldn't restore successfully,
13824 * to give the user an ability to fix an error and retry --
13825 * we keep the saved state file in this case)
13826 */
13827 deleteSavedState = true;
13828 }
13829 }
13830 else if ( oldMachineState == MachineState_Saved
13831 && ( aMachineState == MachineState_PoweredOff
13832 || aMachineState == MachineState_Aborted
13833 || aMachineState == MachineState_Teleported
13834 )
13835 )
13836 {
13837 /*
13838 * delete the saved state after Console::ForgetSavedState() is called
13839 * or if the VM process (owning a direct VM session) crashed while the
13840 * VM was Saved
13841 */
13842
13843 /// @todo (dmik)
13844 // Not sure that deleting the saved state file just because of the
13845 // client death before it attempted to restore the VM is a good
13846 // thing. But when it crashes we need to go to the Aborted state
13847 // which cannot have the saved state file associated... The only
13848 // way to fix this is to make the Aborted condition not a VM state
13849 // but a bool flag: i.e., when a crash occurs, set it to true and
13850 // change the state to PoweredOff or Saved depending on the
13851 // saved state presence.
13852
13853 deleteSavedState = true;
13854 mData->mCurrentStateModified = TRUE;
13855 stsFlags |= SaveSTS_CurStateModified;
13856 }
13857
13858 if ( aMachineState == MachineState_Starting
13859 || aMachineState == MachineState_Restoring
13860 || aMachineState == MachineState_TeleportingIn
13861 )
13862 {
13863 /* set the current state modified flag to indicate that the current
13864 * state is no more identical to the state in the
13865 * current snapshot */
13866 if (!mData->mCurrentSnapshot.isNull())
13867 {
13868 mData->mCurrentStateModified = TRUE;
13869 stsFlags |= SaveSTS_CurStateModified;
13870 }
13871 }
13872
13873 if (deleteSavedState)
13874 {
13875 if (mRemoveSavedState)
13876 {
13877 Assert(!mSSData->strStateFilePath.isEmpty());
13878
13879 // it is safe to delete the saved state file if ...
13880 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13881 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13882 // ... none of the snapshots share the saved state file
13883 )
13884 RTFileDelete(mSSData->strStateFilePath.c_str());
13885 }
13886
13887 mSSData->strStateFilePath.setNull();
13888 stsFlags |= SaveSTS_StateFilePath;
13889 }
13890
13891 /* redirect to the underlying peer machine */
13892 mPeer->setMachineState(aMachineState);
13893
13894 if ( aMachineState == MachineState_PoweredOff
13895 || aMachineState == MachineState_Teleported
13896 || aMachineState == MachineState_Aborted
13897 || aMachineState == MachineState_Saved)
13898 {
13899 /* the machine has stopped execution
13900 * (or the saved state file was adopted) */
13901 stsFlags |= SaveSTS_StateTimeStamp;
13902 }
13903
13904 if ( ( oldMachineState == MachineState_PoweredOff
13905 || oldMachineState == MachineState_Aborted
13906 || oldMachineState == MachineState_Teleported
13907 )
13908 && aMachineState == MachineState_Saved)
13909 {
13910 /* the saved state file was adopted */
13911 Assert(!mSSData->strStateFilePath.isEmpty());
13912 stsFlags |= SaveSTS_StateFilePath;
13913 }
13914
13915#ifdef VBOX_WITH_GUEST_PROPS
13916 if ( aMachineState == MachineState_PoweredOff
13917 || aMachineState == MachineState_Aborted
13918 || aMachineState == MachineState_Teleported)
13919 {
13920 /* Make sure any transient guest properties get removed from the
13921 * property store on shutdown. */
13922
13923 HWData::GuestPropertyList::iterator it;
13924 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13925 if (!fNeedsSaving)
13926 for (it = mHWData->mGuestProperties.begin();
13927 it != mHWData->mGuestProperties.end(); ++it)
13928 if ( (it->mFlags & guestProp::TRANSIENT)
13929 || (it->mFlags & guestProp::TRANSRESET))
13930 {
13931 fNeedsSaving = true;
13932 break;
13933 }
13934 if (fNeedsSaving)
13935 {
13936 mData->mCurrentStateModified = TRUE;
13937 stsFlags |= SaveSTS_CurStateModified;
13938 }
13939 }
13940#endif
13941
13942 rc = saveStateSettings(stsFlags);
13943
13944 if ( ( oldMachineState != MachineState_PoweredOff
13945 && oldMachineState != MachineState_Aborted
13946 && oldMachineState != MachineState_Teleported
13947 )
13948 && ( aMachineState == MachineState_PoweredOff
13949 || aMachineState == MachineState_Aborted
13950 || aMachineState == MachineState_Teleported
13951 )
13952 )
13953 {
13954 /* we've been shut down for any reason */
13955 /* no special action so far */
13956 }
13957
13958 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13959 LogFlowThisFuncLeave();
13960 return rc;
13961}
13962
13963/**
13964 * Sends the current machine state value to the VM process.
13965 *
13966 * @note Locks this object for reading, then calls a client process.
13967 */
13968HRESULT SessionMachine::updateMachineStateOnClient()
13969{
13970 AutoCaller autoCaller(this);
13971 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13972
13973 ComPtr<IInternalSessionControl> directControl;
13974 {
13975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13976 AssertReturn(!!mData, E_FAIL);
13977 directControl = mData->mSession.mDirectControl;
13978
13979 /* directControl may be already set to NULL here in #OnSessionEnd()
13980 * called too early by the direct session process while there is still
13981 * some operation (like deleting the snapshot) in progress. The client
13982 * process in this case is waiting inside Session::close() for the
13983 * "end session" process object to complete, while #uninit() called by
13984 * #checkForDeath() on the Watcher thread is waiting for the pending
13985 * operation to complete. For now, we accept this inconsistent behavior
13986 * and simply do nothing here. */
13987
13988 if (mData->mSession.mState == SessionState_Unlocking)
13989 return S_OK;
13990
13991 AssertReturn(!directControl.isNull(), E_FAIL);
13992 }
13993
13994 return directControl->UpdateMachineState(mData->mMachineState);
13995}
Note: See TracBrowser for help on using the repository browser.

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