VirtualBox

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

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

Stop cancel operation succeeds ref 5903

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 467.5 KB
Line 
1/* $Id: MachineImpl.cpp 43870 2012-11-14 18:04:07Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2012 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#include <iprt/path.h>
68#include <iprt/dir.h>
69#include <iprt/env.h>
70#include <iprt/lockvalidator.h>
71#include <iprt/process.h>
72#include <iprt/cpp/utils.h>
73#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
74#include <iprt/sha.h>
75#include <iprt/string.h>
76
77#include <VBox/com/array.h>
78#include <VBox/com/list.h>
79
80#include <VBox/err.h>
81#include <VBox/param.h>
82#include <VBox/settings.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mState = SessionState_Unlocked;
135}
136
137Machine::Data::~Data()
138{
139 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
140 {
141 RTSemEventMultiDestroy(mMachineStateDepsSem);
142 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
143 }
144 if (pMachineConfigFile)
145 {
146 delete pMachineConfigFile;
147 pMachineConfigFile = NULL;
148 }
149}
150
151/////////////////////////////////////////////////////////////////////////////
152// Machine::HWData structure
153/////////////////////////////////////////////////////////////////////////////
154
155Machine::HWData::HWData()
156{
157 /* default values for a newly created machine */
158 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
159 mMemorySize = 128;
160 mCPUCount = 1;
161 mCPUHotPlugEnabled = false;
162 mMemoryBalloonSize = 0;
163 mPageFusionEnabled = false;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureFile = "Test.webm";
169 mVideoCaptureWidth = 640;
170 mVideoCaptureHeight = 480;
171 mVideoCaptureEnabled = true;
172
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExForceEnabled = false;
183#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
184 mHWVirtExExclusive = false;
185#else
186 mHWVirtExExclusive = true;
187#endif
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mSyntheticCpu = false;
194 mHPETEnabled = false;
195
196 /* default boot order: floppy - DVD - HDD */
197 mBootOrder[0] = DeviceType_Floppy;
198 mBootOrder[1] = DeviceType_DVD;
199 mBootOrder[2] = DeviceType_HardDisk;
200 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
201 mBootOrder[i] = DeviceType_Null;
202
203 mClipboardMode = ClipboardMode_Disabled;
204 mDragAndDropMode = DragAndDropMode_Disabled;
205 mGuestPropertyNotificationPatterns = "";
206
207 mFirmwareType = FirmwareType_BIOS;
208 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
209 mPointingHIDType = PointingHIDType_PS2Mouse;
210 mChipsetType = ChipsetType_PIIX3;
211 mEmulatedUSBCardReaderEnabled = FALSE;
212
213 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
214 mCPUAttached[i] = false;
215
216 mIOCacheEnabled = true;
217 mIOCacheSize = 5; /* 5MB */
218
219 /* Maximum CPU execution cap by default. */
220 mCpuExecutionCap = 100;
221}
222
223Machine::HWData::~HWData()
224{
225}
226
227/////////////////////////////////////////////////////////////////////////////
228// Machine::HDData structure
229/////////////////////////////////////////////////////////////////////////////
230
231Machine::MediaData::MediaData()
232{
233}
234
235Machine::MediaData::~MediaData()
236{
237}
238
239/////////////////////////////////////////////////////////////////////////////
240// Machine class
241/////////////////////////////////////////////////////////////////////////////
242
243// constructor / destructor
244/////////////////////////////////////////////////////////////////////////////
245
246Machine::Machine()
247 : mCollectorGuest(NULL),
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param aOsType OS Type of this machine or NULL.
281 * @param aId UUID for the new machine.
282 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 GuestOSType *aOsType,
291 const Guid &aId,
292 bool fForceOverwrite,
293 bool fDirectoryIncludesUUID)
294{
295 LogFlowThisFuncEnter();
296 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
297
298 /* Enclose the state transition NotReady->InInit->Ready */
299 AutoInitSpan autoInitSpan(this);
300 AssertReturn(autoInitSpan.isOk(), E_FAIL);
301
302 HRESULT rc = initImpl(aParent, strConfigFile);
303 if (FAILED(rc)) return rc;
304
305 rc = tryCreateMachineConfigFile(fForceOverwrite);
306 if (FAILED(rc)) return rc;
307
308 if (SUCCEEDED(rc))
309 {
310 // create an empty machine config
311 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
312
313 rc = initDataAndChildObjects();
314 }
315
316 if (SUCCEEDED(rc))
317 {
318 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
319 mData->mAccessible = TRUE;
320
321 unconst(mData->mUuid) = aId;
322
323 mUserData->s.strName = strName;
324
325 mUserData->s.llGroups = llGroups;
326
327 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
328 // the "name sync" flag determines whether the machine directory gets renamed along
329 // with the machine file; say so if the settings file name is the same as the
330 // settings file parent directory (machine directory)
331 mUserData->s.fNameSync = isInOwnDir();
332
333 // initialize the default snapshots folder
334 rc = COMSETTER(SnapshotFolder)(NULL);
335 AssertComRC(rc);
336
337 if (aOsType)
338 {
339 /* Store OS type */
340 mUserData->s.strOsType = aOsType->id();
341
342 /* Apply BIOS defaults */
343 mBIOSSettings->applyDefaults(aOsType);
344
345 /* Apply network adapters defaults */
346 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
347 mNetworkAdapters[slot]->applyDefaults(aOsType);
348
349 /* Apply serial port defaults */
350 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
351 mSerialPorts[slot]->applyDefaults(aOsType);
352 }
353
354 /* At this point the changing of the current state modification
355 * flag is allowed. */
356 allowStateModification();
357
358 /* commit all changes made during the initialization */
359 commit();
360 }
361
362 /* Confirm a successful initialization when it's the case */
363 if (SUCCEEDED(rc))
364 {
365 if (mData->mAccessible)
366 autoInitSpan.setSucceeded();
367 else
368 autoInitSpan.setLimited();
369 }
370
371 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
372 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
373 mData->mRegistered,
374 mData->mAccessible,
375 rc));
376
377 LogFlowThisFuncLeave();
378
379 return rc;
380}
381
382/**
383 * Initializes a new instance with data from machine XML (formerly Init_Registered).
384 * Gets called in two modes:
385 *
386 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
387 * UUID is specified and we mark the machine as "registered";
388 *
389 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
390 * and the machine remains unregistered until RegisterMachine() is called.
391 *
392 * @param aParent Associated parent object
393 * @param aConfigFile Local file system path to the VM settings file (can
394 * be relative to the VirtualBox config directory).
395 * @param aId UUID of the machine or NULL (see above).
396 *
397 * @return Success indicator. if not S_OK, the machine object is invalid
398 */
399HRESULT Machine::initFromSettings(VirtualBox *aParent,
400 const Utf8Str &strConfigFile,
401 const Guid *aId)
402{
403 LogFlowThisFuncEnter();
404 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
405
406 /* Enclose the state transition NotReady->InInit->Ready */
407 AutoInitSpan autoInitSpan(this);
408 AssertReturn(autoInitSpan.isOk(), E_FAIL);
409
410 HRESULT rc = initImpl(aParent, strConfigFile);
411 if (FAILED(rc)) return rc;
412
413 if (aId)
414 {
415 // loading a registered VM:
416 unconst(mData->mUuid) = *aId;
417 mData->mRegistered = TRUE;
418 // now load the settings from XML:
419 rc = registeredInit();
420 // this calls initDataAndChildObjects() and loadSettings()
421 }
422 else
423 {
424 // opening an unregistered VM (VirtualBox::OpenMachine()):
425 rc = initDataAndChildObjects();
426
427 if (SUCCEEDED(rc))
428 {
429 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
430 mData->mAccessible = TRUE;
431
432 try
433 {
434 // load and parse machine XML; this will throw on XML or logic errors
435 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
436
437 // reject VM UUID duplicates, they can happen if someone
438 // tries to register an already known VM config again
439 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
440 true /* fPermitInaccessible */,
441 false /* aDoSetError */,
442 NULL) != VBOX_E_OBJECT_NOT_FOUND)
443 {
444 throw setError(E_FAIL,
445 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
446 mData->m_strConfigFile.c_str());
447 }
448
449 // use UUID from machine config
450 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
451
452 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
453 NULL /* puuidRegistry */);
454 if (FAILED(rc)) throw rc;
455
456 /* At this point the changing of the current state modification
457 * flag is allowed. */
458 allowStateModification();
459
460 commit();
461 }
462 catch (HRESULT err)
463 {
464 /* we assume that error info is set by the thrower */
465 rc = err;
466 }
467 catch (...)
468 {
469 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
470 }
471 }
472 }
473
474 /* Confirm a successful initialization when it's the case */
475 if (SUCCEEDED(rc))
476 {
477 if (mData->mAccessible)
478 autoInitSpan.setSucceeded();
479 else
480 {
481 autoInitSpan.setLimited();
482
483 // uninit media from this machine's media registry, or else
484 // reloading the settings will fail
485 mParent->unregisterMachineMedia(getId());
486 }
487 }
488
489 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
490 "rc=%08X\n",
491 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
492 mData->mRegistered, mData->mAccessible, rc));
493
494 LogFlowThisFuncLeave();
495
496 return rc;
497}
498
499/**
500 * Initializes a new instance from a machine config that is already in memory
501 * (import OVF case). Since we are importing, the UUID in the machine
502 * config is ignored and we always generate a fresh one.
503 *
504 * @param strName Name for the new machine; this overrides what is specified in config and is used
505 * for the settings file as well.
506 * @param config Machine configuration loaded and parsed from XML.
507 *
508 * @return Success indicator. if not S_OK, the machine object is invalid
509 */
510HRESULT Machine::init(VirtualBox *aParent,
511 const Utf8Str &strName,
512 const settings::MachineConfigFile &config)
513{
514 LogFlowThisFuncEnter();
515
516 /* Enclose the state transition NotReady->InInit->Ready */
517 AutoInitSpan autoInitSpan(this);
518 AssertReturn(autoInitSpan.isOk(), E_FAIL);
519
520 Utf8Str strConfigFile;
521 aParent->getDefaultMachineFolder(strConfigFile);
522 strConfigFile.append(RTPATH_DELIMITER);
523 strConfigFile.append(strName);
524 strConfigFile.append(RTPATH_DELIMITER);
525 strConfigFile.append(strName);
526 strConfigFile.append(".vbox");
527
528 HRESULT rc = initImpl(aParent, strConfigFile);
529 if (FAILED(rc)) return rc;
530
531 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
532 if (FAILED(rc)) return rc;
533
534 rc = initDataAndChildObjects();
535
536 if (SUCCEEDED(rc))
537 {
538 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
539 mData->mAccessible = TRUE;
540
541 // create empty machine config for instance data
542 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
543
544 // generate fresh UUID, ignore machine config
545 unconst(mData->mUuid).create();
546
547 rc = loadMachineDataFromSettings(config,
548 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
549
550 // override VM name as well, it may be different
551 mUserData->s.strName = strName;
552
553 if (SUCCEEDED(rc))
554 {
555 /* At this point the changing of the current state modification
556 * flag is allowed. */
557 allowStateModification();
558
559 /* commit all changes made during the initialization */
560 commit();
561 }
562 }
563
564 /* Confirm a successful initialization when it's the case */
565 if (SUCCEEDED(rc))
566 {
567 if (mData->mAccessible)
568 autoInitSpan.setSucceeded();
569 else
570 {
571 autoInitSpan.setLimited();
572
573 // uninit media from this machine's media registry, or else
574 // reloading the settings will fail
575 mParent->unregisterMachineMedia(getId());
576 }
577 }
578
579 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
580 "rc=%08X\n",
581 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
582 mData->mRegistered, mData->mAccessible, rc));
583
584 LogFlowThisFuncLeave();
585
586 return rc;
587}
588
589/**
590 * Shared code between the various init() implementations.
591 * @param aParent
592 * @return
593 */
594HRESULT Machine::initImpl(VirtualBox *aParent,
595 const Utf8Str &strConfigFile)
596{
597 LogFlowThisFuncEnter();
598
599 AssertReturn(aParent, E_INVALIDARG);
600 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
601
602 HRESULT rc = S_OK;
603
604 /* share the parent weakly */
605 unconst(mParent) = aParent;
606
607 /* allocate the essential machine data structure (the rest will be
608 * allocated later by initDataAndChildObjects() */
609 mData.allocate();
610
611 /* memorize the config file name (as provided) */
612 mData->m_strConfigFile = strConfigFile;
613
614 /* get the full file name */
615 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
616 if (RT_FAILURE(vrc1))
617 return setError(VBOX_E_FILE_ERROR,
618 tr("Invalid machine settings file name '%s' (%Rrc)"),
619 strConfigFile.c_str(),
620 vrc1);
621
622 LogFlowThisFuncLeave();
623
624 return rc;
625}
626
627/**
628 * Tries to create a machine settings file in the path stored in the machine
629 * instance data. Used when a new machine is created to fail gracefully if
630 * the settings file could not be written (e.g. because machine dir is read-only).
631 * @return
632 */
633HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
634{
635 HRESULT rc = S_OK;
636
637 // when we create a new machine, we must be able to create the settings file
638 RTFILE f = NIL_RTFILE;
639 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
640 if ( RT_SUCCESS(vrc)
641 || vrc == VERR_SHARING_VIOLATION
642 )
643 {
644 if (RT_SUCCESS(vrc))
645 RTFileClose(f);
646 if (!fForceOverwrite)
647 rc = setError(VBOX_E_FILE_ERROR,
648 tr("Machine settings file '%s' already exists"),
649 mData->m_strConfigFileFull.c_str());
650 else
651 {
652 /* try to delete the config file, as otherwise the creation
653 * of a new settings file will fail. */
654 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
655 if (RT_FAILURE(vrc2))
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Could not delete the existing settings file '%s' (%Rrc)"),
658 mData->m_strConfigFileFull.c_str(), vrc2);
659 }
660 }
661 else if ( vrc != VERR_FILE_NOT_FOUND
662 && vrc != VERR_PATH_NOT_FOUND
663 )
664 rc = setError(VBOX_E_FILE_ERROR,
665 tr("Invalid machine settings file name '%s' (%Rrc)"),
666 mData->m_strConfigFileFull.c_str(),
667 vrc);
668 return rc;
669}
670
671/**
672 * Initializes the registered machine by loading the settings file.
673 * This method is separated from #init() in order to make it possible to
674 * retry the operation after VirtualBox startup instead of refusing to
675 * startup the whole VirtualBox server in case if the settings file of some
676 * registered VM is invalid or inaccessible.
677 *
678 * @note Must be always called from this object's write lock
679 * (unless called from #init() that doesn't need any locking).
680 * @note Locks the mUSBController method for writing.
681 * @note Subclasses must not call this method.
682 */
683HRESULT Machine::registeredInit()
684{
685 AssertReturn(!isSessionMachine(), E_FAIL);
686 AssertReturn(!isSnapshotMachine(), E_FAIL);
687 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL);
688 AssertReturn(!mData->mAccessible, E_FAIL);
689
690 HRESULT rc = initDataAndChildObjects();
691
692 if (SUCCEEDED(rc))
693 {
694 /* Temporarily reset the registered flag in order to let setters
695 * potentially called from loadSettings() succeed (isMutable() used in
696 * all setters will return FALSE for a Machine instance if mRegistered
697 * is TRUE). */
698 mData->mRegistered = FALSE;
699
700 try
701 {
702 // load and parse machine XML; this will throw on XML or logic errors
703 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
704
705 if (mData->mUuid != mData->pMachineConfigFile->uuid)
706 throw setError(E_FAIL,
707 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
708 mData->pMachineConfigFile->uuid.raw(),
709 mData->m_strConfigFileFull.c_str(),
710 mData->mUuid.toString().c_str(),
711 mParent->settingsFilePath().c_str());
712
713 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
714 NULL /* const Guid *puuidRegistry */);
715 if (FAILED(rc)) throw rc;
716 }
717 catch (HRESULT err)
718 {
719 /* we assume that error info is set by the thrower */
720 rc = err;
721 }
722 catch (...)
723 {
724 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
725 }
726
727 /* Restore the registered flag (even on failure) */
728 mData->mRegistered = TRUE;
729 }
730
731 if (SUCCEEDED(rc))
732 {
733 /* Set mAccessible to TRUE only if we successfully locked and loaded
734 * the settings file */
735 mData->mAccessible = TRUE;
736
737 /* commit all changes made during loading the settings file */
738 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
739 /// @todo r=klaus for some reason the settings loading logic backs up
740 // the settings, and therefore a commit is needed. Should probably be changed.
741 }
742 else
743 {
744 /* If the machine is registered, then, instead of returning a
745 * failure, we mark it as inaccessible and set the result to
746 * success to give it a try later */
747
748 /* fetch the current error info */
749 mData->mAccessError = com::ErrorInfo();
750 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
751 mData->mUuid.raw(),
752 mData->mAccessError.getText().raw()));
753
754 /* rollback all changes */
755 rollback(false /* aNotify */);
756
757 // uninit media from this machine's media registry, or else
758 // reloading the settings will fail
759 mParent->unregisterMachineMedia(getId());
760
761 /* uninitialize the common part to make sure all data is reset to
762 * default (null) values */
763 uninitDataAndChildObjects();
764
765 rc = S_OK;
766 }
767
768 return rc;
769}
770
771/**
772 * Uninitializes the instance.
773 * Called either from FinalRelease() or by the parent when it gets destroyed.
774 *
775 * @note The caller of this method must make sure that this object
776 * a) doesn't have active callers on the current thread and b) is not locked
777 * by the current thread; otherwise uninit() will hang either a) due to
778 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
779 * a dead-lock caused by this thread waiting for all callers on the other
780 * threads are done but preventing them from doing so by holding a lock.
781 */
782void Machine::uninit()
783{
784 LogFlowThisFuncEnter();
785
786 Assert(!isWriteLockOnCurrentThread());
787
788 Assert(!uRegistryNeedsSaving);
789 if (uRegistryNeedsSaving)
790 {
791 AutoCaller autoCaller(this);
792 if (SUCCEEDED(autoCaller.rc()))
793 {
794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
795 saveSettings(NULL, Machine::SaveS_Force);
796 }
797 }
798
799 /* Enclose the state transition Ready->InUninit->NotReady */
800 AutoUninitSpan autoUninitSpan(this);
801 if (autoUninitSpan.uninitDone())
802 return;
803
804 Assert(!isSnapshotMachine());
805 Assert(!isSessionMachine());
806 Assert(!!mData);
807
808 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
809 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
810
811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
812
813 if (!mData->mSession.mMachine.isNull())
814 {
815 /* Theoretically, this can only happen if the VirtualBox server has been
816 * terminated while there were clients running that owned open direct
817 * sessions. Since in this case we are definitely called by
818 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
819 * won't happen on the client watcher thread (because it does
820 * VirtualBox::addCaller() for the duration of the
821 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
822 * cannot happen until the VirtualBox caller is released). This is
823 * important, because SessionMachine::uninit() cannot correctly operate
824 * after we return from this method (it expects the Machine instance is
825 * still valid). We'll call it ourselves below.
826 */
827 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
828 (SessionMachine*)mData->mSession.mMachine));
829
830 if (Global::IsOnlineOrTransient(mData->mMachineState))
831 {
832 LogWarningThisFunc(("Setting state to Aborted!\n"));
833 /* set machine state using SessionMachine reimplementation */
834 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
835 }
836
837 /*
838 * Uninitialize SessionMachine using public uninit() to indicate
839 * an unexpected uninitialization.
840 */
841 mData->mSession.mMachine->uninit();
842 /* SessionMachine::uninit() must set mSession.mMachine to null */
843 Assert(mData->mSession.mMachine.isNull());
844 }
845
846 // uninit media from this machine's media registry, if they're still there
847 Guid uuidMachine(getId());
848
849 /* the lock is no more necessary (SessionMachine is uninitialized) */
850 alock.release();
851
852 /* XXX This will fail with
853 * "cannot be closed because it is still attached to 1 virtual machines"
854 * because at this point we did not call uninitDataAndChildObjects() yet
855 * and therefore also removeBackReference() for all these mediums was not called! */
856 if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
857 mParent->unregisterMachineMedia(uuidMachine);
858
859 // has machine been modified?
860 if (mData->flModifications)
861 {
862 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
863 rollback(false /* aNotify */);
864 }
865
866 if (mData->mAccessible)
867 uninitDataAndChildObjects();
868
869 /* free the essential data structure last */
870 mData.free();
871
872 LogFlowThisFuncLeave();
873}
874
875// IMachine properties
876/////////////////////////////////////////////////////////////////////////////
877
878STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
879{
880 CheckComArgOutPointerValid(aParent);
881
882 AutoLimitedCaller autoCaller(this);
883 if (FAILED(autoCaller.rc())) return autoCaller.rc();
884
885 /* mParent is constant during life time, no need to lock */
886 ComObjPtr<VirtualBox> pVirtualBox(mParent);
887 pVirtualBox.queryInterfaceTo(aParent);
888
889 return S_OK;
890}
891
892STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
893{
894 CheckComArgOutPointerValid(aAccessible);
895
896 AutoLimitedCaller autoCaller(this);
897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
898
899 LogFlowThisFunc(("ENTER\n"));
900
901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
902
903 HRESULT rc = S_OK;
904
905 if (!mData->mAccessible)
906 {
907 /* try to initialize the VM once more if not accessible */
908
909 AutoReinitSpan autoReinitSpan(this);
910 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
911
912#ifdef DEBUG
913 LogFlowThisFunc(("Dumping media backreferences\n"));
914 mParent->dumpAllBackRefs();
915#endif
916
917 if (mData->pMachineConfigFile)
918 {
919 // reset the XML file to force loadSettings() (called from registeredInit())
920 // to parse it again; the file might have changed
921 delete mData->pMachineConfigFile;
922 mData->pMachineConfigFile = NULL;
923 }
924
925 rc = registeredInit();
926
927 if (SUCCEEDED(rc) && mData->mAccessible)
928 {
929 autoReinitSpan.setSucceeded();
930
931 /* make sure interesting parties will notice the accessibility
932 * state change */
933 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
934 mParent->onMachineDataChange(mData->mUuid);
935 }
936 }
937
938 if (SUCCEEDED(rc))
939 *aAccessible = mData->mAccessible;
940
941 LogFlowThisFuncLeave();
942
943 return rc;
944}
945
946STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
947{
948 CheckComArgOutPointerValid(aAccessError);
949
950 AutoLimitedCaller autoCaller(this);
951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
952
953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
954
955 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
956 {
957 /* return shortly */
958 aAccessError = NULL;
959 return S_OK;
960 }
961
962 HRESULT rc = S_OK;
963
964 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
965 rc = errorInfo.createObject();
966 if (SUCCEEDED(rc))
967 {
968 errorInfo->init(mData->mAccessError.getResultCode(),
969 mData->mAccessError.getInterfaceID().ref(),
970 Utf8Str(mData->mAccessError.getComponent()).c_str(),
971 Utf8Str(mData->mAccessError.getText()));
972 rc = errorInfo.queryInterfaceTo(aAccessError);
973 }
974
975 return rc;
976}
977
978STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
979{
980 CheckComArgOutPointerValid(aName);
981
982 AutoCaller autoCaller(this);
983 if (FAILED(autoCaller.rc())) return autoCaller.rc();
984
985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
986
987 mUserData->s.strName.cloneTo(aName);
988
989 return S_OK;
990}
991
992STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
993{
994 CheckComArgStrNotEmptyOrNull(aName);
995
996 AutoCaller autoCaller(this);
997 if (FAILED(autoCaller.rc())) return autoCaller.rc();
998
999 // prohibit setting a UUID only as the machine name, or else it can
1000 // never be found by findMachine()
1001 Guid test(aName);
1002 if (test.isNotEmpty())
1003 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1004
1005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1006
1007 HRESULT rc = checkStateDependency(MutableStateDep);
1008 if (FAILED(rc)) return rc;
1009
1010 setModified(IsModified_MachineData);
1011 mUserData.backup();
1012 mUserData->s.strName = aName;
1013
1014 return S_OK;
1015}
1016
1017STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1018{
1019 CheckComArgOutPointerValid(aDescription);
1020
1021 AutoCaller autoCaller(this);
1022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1023
1024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 mUserData->s.strDescription.cloneTo(aDescription);
1027
1028 return S_OK;
1029}
1030
1031STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1032{
1033 AutoCaller autoCaller(this);
1034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1035
1036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1037
1038 // this can be done in principle in any state as it doesn't affect the VM
1039 // significantly, but play safe by not messing around while complex
1040 // activities are going on
1041 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1042 if (FAILED(rc)) return rc;
1043
1044 setModified(IsModified_MachineData);
1045 mUserData.backup();
1046 mUserData->s.strDescription = aDescription;
1047
1048 return S_OK;
1049}
1050
1051STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1052{
1053 CheckComArgOutPointerValid(aId);
1054
1055 AutoLimitedCaller autoCaller(this);
1056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1057
1058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1059
1060 mData->mUuid.toUtf16().cloneTo(aId);
1061
1062 return S_OK;
1063}
1064
1065STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1066{
1067 CheckComArgOutSafeArrayPointerValid(aGroups);
1068
1069 AutoCaller autoCaller(this);
1070 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1071
1072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1073 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1074 size_t i = 0;
1075 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1076 it != mUserData->s.llGroups.end();
1077 ++it, i++)
1078 {
1079 Bstr tmp = *it;
1080 tmp.cloneTo(&groups[i]);
1081 }
1082 groups.detachTo(ComSafeArrayOutArg(aGroups));
1083
1084 return S_OK;
1085}
1086
1087STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1088{
1089 AutoCaller autoCaller(this);
1090 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1091
1092 StringsList llGroups;
1093 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1094 if (FAILED(rc))
1095 return rc;
1096
1097 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1098
1099 // changing machine groups is possible while the VM is offline
1100 rc = checkStateDependency(OfflineStateDep);
1101 if (FAILED(rc)) return rc;
1102
1103 setModified(IsModified_MachineData);
1104 mUserData.backup();
1105 mUserData->s.llGroups = llGroups;
1106
1107 return S_OK;
1108}
1109
1110STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1111{
1112 CheckComArgOutPointerValid(aOSTypeId);
1113
1114 AutoCaller autoCaller(this);
1115 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1116
1117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 mUserData->s.strOsType.cloneTo(aOSTypeId);
1120
1121 return S_OK;
1122}
1123
1124STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1125{
1126 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1127
1128 AutoCaller autoCaller(this);
1129 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1130
1131 /* look up the object by Id to check it is valid */
1132 ComPtr<IGuestOSType> guestOSType;
1133 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1134 if (FAILED(rc)) return rc;
1135
1136 /* when setting, always use the "etalon" value for consistency -- lookup
1137 * by ID is case-insensitive and the input value may have different case */
1138 Bstr osTypeId;
1139 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1140 if (FAILED(rc)) return rc;
1141
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 rc = checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 setModified(IsModified_MachineData);
1148 mUserData.backup();
1149 mUserData->s.strOsType = osTypeId;
1150
1151 return S_OK;
1152}
1153
1154
1155STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1156{
1157 CheckComArgOutPointerValid(aFirmwareType);
1158
1159 AutoCaller autoCaller(this);
1160 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1161
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aFirmwareType = mHWData->mFirmwareType;
1165
1166 return S_OK;
1167}
1168
1169STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1170{
1171 AutoCaller autoCaller(this);
1172 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 HRESULT rc = checkStateDependency(MutableStateDep);
1176 if (FAILED(rc)) return rc;
1177
1178 setModified(IsModified_MachineData);
1179 mHWData.backup();
1180 mHWData->mFirmwareType = aFirmwareType;
1181
1182 return S_OK;
1183}
1184
1185STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1186{
1187 CheckComArgOutPointerValid(aKeyboardHIDType);
1188
1189 AutoCaller autoCaller(this);
1190 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1191
1192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1193
1194 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1195
1196 return S_OK;
1197}
1198
1199STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1200{
1201 AutoCaller autoCaller(this);
1202 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 HRESULT rc = checkStateDependency(MutableStateDep);
1206 if (FAILED(rc)) return rc;
1207
1208 setModified(IsModified_MachineData);
1209 mHWData.backup();
1210 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1211
1212 return S_OK;
1213}
1214
1215STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1216{
1217 CheckComArgOutPointerValid(aPointingHIDType);
1218
1219 AutoCaller autoCaller(this);
1220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1221
1222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1223
1224 *aPointingHIDType = mHWData->mPointingHIDType;
1225
1226 return S_OK;
1227}
1228
1229STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1230{
1231 AutoCaller autoCaller(this);
1232 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 HRESULT rc = checkStateDependency(MutableStateDep);
1236 if (FAILED(rc)) return rc;
1237
1238 setModified(IsModified_MachineData);
1239 mHWData.backup();
1240 mHWData->mPointingHIDType = aPointingHIDType;
1241
1242 return S_OK;
1243}
1244
1245STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1246{
1247 CheckComArgOutPointerValid(aChipsetType);
1248
1249 AutoCaller autoCaller(this);
1250 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1251
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aChipsetType = mHWData->mChipsetType;
1255
1256 return S_OK;
1257}
1258
1259STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1260{
1261 AutoCaller autoCaller(this);
1262 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 HRESULT rc = checkStateDependency(MutableStateDep);
1266 if (FAILED(rc)) return rc;
1267
1268 if (aChipsetType != mHWData->mChipsetType)
1269 {
1270 setModified(IsModified_MachineData);
1271 mHWData.backup();
1272 mHWData->mChipsetType = aChipsetType;
1273
1274 // Resize network adapter array, to be finalized on commit/rollback.
1275 // We must not throw away entries yet, otherwise settings are lost
1276 // without a way to roll back.
1277 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1278 uint32_t oldCount = mNetworkAdapters.size();
1279 if (newCount > oldCount)
1280 {
1281 mNetworkAdapters.resize(newCount);
1282 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1283 {
1284 unconst(mNetworkAdapters[slot]).createObject();
1285 mNetworkAdapters[slot]->init(this, slot);
1286 }
1287 }
1288 }
1289
1290 return S_OK;
1291}
1292
1293STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1294{
1295 CheckComArgOutPointerValid(aHWVersion);
1296
1297 AutoCaller autoCaller(this);
1298 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1299
1300 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1301
1302 mHWData->mHWVersion.cloneTo(aHWVersion);
1303
1304 return S_OK;
1305}
1306
1307STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1308{
1309 /* check known version */
1310 Utf8Str hwVersion = aHWVersion;
1311 if ( hwVersion.compare("1") != 0
1312 && hwVersion.compare("2") != 0)
1313 return setError(E_INVALIDARG,
1314 tr("Invalid hardware version: %ls\n"), aHWVersion);
1315
1316 AutoCaller autoCaller(this);
1317 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1318
1319 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 HRESULT rc = checkStateDependency(MutableStateDep);
1322 if (FAILED(rc)) return rc;
1323
1324 setModified(IsModified_MachineData);
1325 mHWData.backup();
1326 mHWData->mHWVersion = hwVersion;
1327
1328 return S_OK;
1329}
1330
1331STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1332{
1333 CheckComArgOutPointerValid(aUUID);
1334
1335 AutoCaller autoCaller(this);
1336 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1337
1338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1339
1340 if (!mHWData->mHardwareUUID.isEmpty())
1341 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1342 else
1343 mData->mUuid.toUtf16().cloneTo(aUUID);
1344
1345 return S_OK;
1346}
1347
1348STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1349{
1350 Guid hardwareUUID(aUUID);
1351 if (hardwareUUID.isEmpty())
1352 return E_INVALIDARG;
1353
1354 AutoCaller autoCaller(this);
1355 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1356
1357 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1358
1359 HRESULT rc = checkStateDependency(MutableStateDep);
1360 if (FAILED(rc)) return rc;
1361
1362 setModified(IsModified_MachineData);
1363 mHWData.backup();
1364 if (hardwareUUID == mData->mUuid)
1365 mHWData->mHardwareUUID.clear();
1366 else
1367 mHWData->mHardwareUUID = hardwareUUID;
1368
1369 return S_OK;
1370}
1371
1372STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1373{
1374 CheckComArgOutPointerValid(memorySize);
1375
1376 AutoCaller autoCaller(this);
1377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1378
1379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1380
1381 *memorySize = mHWData->mMemorySize;
1382
1383 return S_OK;
1384}
1385
1386STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1387{
1388 /* check RAM limits */
1389 if ( memorySize < MM_RAM_MIN_IN_MB
1390 || memorySize > MM_RAM_MAX_IN_MB
1391 )
1392 return setError(E_INVALIDARG,
1393 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1394 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1395
1396 AutoCaller autoCaller(this);
1397 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1398
1399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1400
1401 HRESULT rc = checkStateDependency(MutableStateDep);
1402 if (FAILED(rc)) return rc;
1403
1404 setModified(IsModified_MachineData);
1405 mHWData.backup();
1406 mHWData->mMemorySize = memorySize;
1407
1408 return S_OK;
1409}
1410
1411STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1412{
1413 CheckComArgOutPointerValid(CPUCount);
1414
1415 AutoCaller autoCaller(this);
1416 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1417
1418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1419
1420 *CPUCount = mHWData->mCPUCount;
1421
1422 return S_OK;
1423}
1424
1425STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1426{
1427 /* check CPU limits */
1428 if ( CPUCount < SchemaDefs::MinCPUCount
1429 || CPUCount > SchemaDefs::MaxCPUCount
1430 )
1431 return setError(E_INVALIDARG,
1432 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1433 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1434
1435 AutoCaller autoCaller(this);
1436 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1437
1438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1441 if (mHWData->mCPUHotPlugEnabled)
1442 {
1443 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1444 {
1445 if (mHWData->mCPUAttached[idx])
1446 return setError(E_INVALIDARG,
1447 tr("There is still a CPU attached to socket %lu."
1448 "Detach the CPU before removing the socket"),
1449 CPUCount, idx+1);
1450 }
1451 }
1452
1453 HRESULT rc = checkStateDependency(MutableStateDep);
1454 if (FAILED(rc)) return rc;
1455
1456 setModified(IsModified_MachineData);
1457 mHWData.backup();
1458 mHWData->mCPUCount = CPUCount;
1459
1460 return S_OK;
1461}
1462
1463STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1464{
1465 CheckComArgOutPointerValid(aExecutionCap);
1466
1467 AutoCaller autoCaller(this);
1468 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1469
1470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1471
1472 *aExecutionCap = mHWData->mCpuExecutionCap;
1473
1474 return S_OK;
1475}
1476
1477STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1478{
1479 HRESULT rc = S_OK;
1480
1481 /* check throttle limits */
1482 if ( aExecutionCap < 1
1483 || aExecutionCap > 100
1484 )
1485 return setError(E_INVALIDARG,
1486 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1487 aExecutionCap, 1, 100);
1488
1489 AutoCaller autoCaller(this);
1490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1491
1492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1493
1494 alock.release();
1495 rc = onCPUExecutionCapChange(aExecutionCap);
1496 alock.acquire();
1497 if (FAILED(rc)) return rc;
1498
1499 setModified(IsModified_MachineData);
1500 mHWData.backup();
1501 mHWData->mCpuExecutionCap = aExecutionCap;
1502
1503 /* Save settings if online - todo why is this required?? */
1504 if (Global::IsOnline(mData->mMachineState))
1505 saveSettings(NULL);
1506
1507 return S_OK;
1508}
1509
1510
1511STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1512{
1513 CheckComArgOutPointerValid(enabled);
1514
1515 AutoCaller autoCaller(this);
1516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1517
1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1519
1520 *enabled = mHWData->mCPUHotPlugEnabled;
1521
1522 return S_OK;
1523}
1524
1525STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1526{
1527 HRESULT rc = S_OK;
1528
1529 AutoCaller autoCaller(this);
1530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1531
1532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1533
1534 rc = checkStateDependency(MutableStateDep);
1535 if (FAILED(rc)) return rc;
1536
1537 if (mHWData->mCPUHotPlugEnabled != enabled)
1538 {
1539 if (enabled)
1540 {
1541 setModified(IsModified_MachineData);
1542 mHWData.backup();
1543
1544 /* Add the amount of CPUs currently attached */
1545 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1546 {
1547 mHWData->mCPUAttached[i] = true;
1548 }
1549 }
1550 else
1551 {
1552 /*
1553 * We can disable hotplug only if the amount of maximum CPUs is equal
1554 * to the amount of attached CPUs
1555 */
1556 unsigned cCpusAttached = 0;
1557 unsigned iHighestId = 0;
1558
1559 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1560 {
1561 if (mHWData->mCPUAttached[i])
1562 {
1563 cCpusAttached++;
1564 iHighestId = i;
1565 }
1566 }
1567
1568 if ( (cCpusAttached != mHWData->mCPUCount)
1569 || (iHighestId >= mHWData->mCPUCount))
1570 return setError(E_INVALIDARG,
1571 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1572
1573 setModified(IsModified_MachineData);
1574 mHWData.backup();
1575 }
1576 }
1577
1578 mHWData->mCPUHotPlugEnabled = enabled;
1579
1580 return rc;
1581}
1582
1583STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1584{
1585#ifdef VBOX_WITH_USB_CARDREADER
1586 CheckComArgOutPointerValid(enabled);
1587
1588 AutoCaller autoCaller(this);
1589 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1590
1591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1592
1593 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1594
1595 return S_OK;
1596#else
1597 NOREF(enabled);
1598 return E_NOTIMPL;
1599#endif
1600}
1601
1602STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1603{
1604#ifdef VBOX_WITH_USB_CARDREADER
1605 AutoCaller autoCaller(this);
1606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1607 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1608
1609 HRESULT rc = checkStateDependency(MutableStateDep);
1610 if (FAILED(rc)) return rc;
1611
1612 setModified(IsModified_MachineData);
1613 mHWData.backup();
1614 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1615
1616 return S_OK;
1617#else
1618 NOREF(enabled);
1619 return E_NOTIMPL;
1620#endif
1621}
1622
1623STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1624{
1625 NOREF(enabled);
1626 return E_NOTIMPL;
1627}
1628
1629STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1630{
1631 NOREF(enabled);
1632 return E_NOTIMPL;
1633}
1634
1635STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1636{
1637 CheckComArgOutPointerValid(enabled);
1638
1639 AutoCaller autoCaller(this);
1640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1642
1643 *enabled = mHWData->mHPETEnabled;
1644
1645 return S_OK;
1646}
1647
1648STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1649{
1650 HRESULT rc = S_OK;
1651
1652 AutoCaller autoCaller(this);
1653 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1655
1656 rc = checkStateDependency(MutableStateDep);
1657 if (FAILED(rc)) return rc;
1658
1659 setModified(IsModified_MachineData);
1660 mHWData.backup();
1661
1662 mHWData->mHPETEnabled = enabled;
1663
1664 return rc;
1665}
1666
1667STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled)
1668{
1669 AutoCaller autoCaller(this);
1670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1671
1672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1673
1674 *fEnabled = mHWData->mVideoCaptureEnabled;
1675 return S_OK;
1676}
1677
1678STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1679{
1680 AutoCaller autoCaller(this);
1681 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1682
1683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1684 mHWData->mVideoCaptureEnabled = fEnabled;
1685 return S_OK;
1686}
1687
1688STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1689{
1690 AutoCaller autoCaller(this);
1691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1692
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694 mHWData->mVideoCaptureFile.cloneTo(apFile);
1695 return S_OK;
1696}
1697
1698STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1699{
1700 Utf8Str strFile(aFile);
1701 AutoCaller autoCaller(this);
1702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705 if(strFile.isEmpty())
1706 strFile = "VideoCap.webm";
1707 mHWData->mVideoCaptureFile = strFile;
1708 return S_OK;
1709}
1710
1711
1712STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1713{
1714 AutoCaller autoCaller(this);
1715 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1716
1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1718 *ulHorzRes = mHWData->mVideoCaptureWidth;
1719 return S_OK;
1720}
1721
1722STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1723{
1724 AutoCaller autoCaller(this);
1725 if (FAILED(autoCaller.rc()))
1726 {
1727 LogFlow(("Autolocked failed\n"));
1728 return autoCaller.rc();
1729 }
1730
1731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1732 mHWData->mVideoCaptureWidth = ulHorzRes;
1733 return S_OK;
1734}
1735
1736STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1737{
1738 AutoCaller autoCaller(this);
1739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1740
1741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1742 *ulVertRes = mHWData->mVideoCaptureHeight;
1743 return S_OK;
1744}
1745
1746STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1747{
1748 AutoCaller autoCaller(this);
1749 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1750
1751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1752 mHWData->mVideoCaptureHeight = ulVertRes;
1753 return S_OK;
1754}
1755
1756STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1757{
1758 CheckComArgOutPointerValid(memorySize);
1759
1760 AutoCaller autoCaller(this);
1761 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1762
1763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1764
1765 *memorySize = mHWData->mVRAMSize;
1766
1767 return S_OK;
1768}
1769
1770STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1771{
1772 /* check VRAM limits */
1773 if (memorySize < SchemaDefs::MinGuestVRAM ||
1774 memorySize > SchemaDefs::MaxGuestVRAM)
1775 return setError(E_INVALIDARG,
1776 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1777 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1778
1779 AutoCaller autoCaller(this);
1780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1781
1782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1783
1784 HRESULT rc = checkStateDependency(MutableStateDep);
1785 if (FAILED(rc)) return rc;
1786
1787 setModified(IsModified_MachineData);
1788 mHWData.backup();
1789 mHWData->mVRAMSize = memorySize;
1790
1791 return S_OK;
1792}
1793
1794/** @todo this method should not be public */
1795STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1796{
1797 CheckComArgOutPointerValid(memoryBalloonSize);
1798
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1805
1806 return S_OK;
1807}
1808
1809/**
1810 * Set the memory balloon size.
1811 *
1812 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1813 * we have to make sure that we never call IGuest from here.
1814 */
1815STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1816{
1817 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1818#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1819 /* check limits */
1820 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1821 return setError(E_INVALIDARG,
1822 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1823 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1824
1825 AutoCaller autoCaller(this);
1826 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1827
1828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1829
1830 setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1833
1834 return S_OK;
1835#else
1836 NOREF(memoryBalloonSize);
1837 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1838#endif
1839}
1840
1841STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1842{
1843 CheckComArgOutPointerValid(enabled);
1844
1845 AutoCaller autoCaller(this);
1846 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1847
1848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1849
1850 *enabled = mHWData->mPageFusionEnabled;
1851 return S_OK;
1852}
1853
1854STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1855{
1856#ifdef VBOX_WITH_PAGE_SHARING
1857 AutoCaller autoCaller(this);
1858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1859
1860 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1861
1862 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1863 setModified(IsModified_MachineData);
1864 mHWData.backup();
1865 mHWData->mPageFusionEnabled = enabled;
1866 return S_OK;
1867#else
1868 NOREF(enabled);
1869 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1870#endif
1871}
1872
1873STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1874{
1875 CheckComArgOutPointerValid(enabled);
1876
1877 AutoCaller autoCaller(this);
1878 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1879
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 *enabled = mHWData->mAccelerate3DEnabled;
1883
1884 return S_OK;
1885}
1886
1887STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1888{
1889 AutoCaller autoCaller(this);
1890 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1891
1892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1893
1894 HRESULT rc = checkStateDependency(MutableStateDep);
1895 if (FAILED(rc)) return rc;
1896
1897 /** @todo check validity! */
1898
1899 setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mAccelerate3DEnabled = enable;
1902
1903 return S_OK;
1904}
1905
1906
1907STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1908{
1909 CheckComArgOutPointerValid(enabled);
1910
1911 AutoCaller autoCaller(this);
1912 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1913
1914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1915
1916 *enabled = mHWData->mAccelerate2DVideoEnabled;
1917
1918 return S_OK;
1919}
1920
1921STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1922{
1923 AutoCaller autoCaller(this);
1924 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1925
1926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1927
1928 HRESULT rc = checkStateDependency(MutableStateDep);
1929 if (FAILED(rc)) return rc;
1930
1931 /** @todo check validity! */
1932
1933 setModified(IsModified_MachineData);
1934 mHWData.backup();
1935 mHWData->mAccelerate2DVideoEnabled = enable;
1936
1937 return S_OK;
1938}
1939
1940STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1941{
1942 CheckComArgOutPointerValid(monitorCount);
1943
1944 AutoCaller autoCaller(this);
1945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1946
1947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1948
1949 *monitorCount = mHWData->mMonitorCount;
1950
1951 return S_OK;
1952}
1953
1954STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1955{
1956 /* make sure monitor count is a sensible number */
1957 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1958 return setError(E_INVALIDARG,
1959 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1960 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
1961
1962 AutoCaller autoCaller(this);
1963 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1964
1965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1966
1967 HRESULT rc = checkStateDependency(MutableStateDep);
1968 if (FAILED(rc)) return rc;
1969
1970 setModified(IsModified_MachineData);
1971 mHWData.backup();
1972 mHWData->mMonitorCount = monitorCount;
1973
1974 return S_OK;
1975}
1976
1977STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
1978{
1979 CheckComArgOutPointerValid(biosSettings);
1980
1981 AutoCaller autoCaller(this);
1982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1983
1984 /* mBIOSSettings is constant during life time, no need to lock */
1985 mBIOSSettings.queryInterfaceTo(biosSettings);
1986
1987 return S_OK;
1988}
1989
1990STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
1991{
1992 CheckComArgOutPointerValid(aVal);
1993
1994 AutoCaller autoCaller(this);
1995 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1996
1997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 switch(property)
2000 {
2001 case CPUPropertyType_PAE:
2002 *aVal = mHWData->mPAEEnabled;
2003 break;
2004
2005 case CPUPropertyType_Synthetic:
2006 *aVal = mHWData->mSyntheticCpu;
2007 break;
2008
2009 default:
2010 return E_INVALIDARG;
2011 }
2012 return S_OK;
2013}
2014
2015STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2016{
2017 AutoCaller autoCaller(this);
2018 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2019
2020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2021
2022 HRESULT rc = checkStateDependency(MutableStateDep);
2023 if (FAILED(rc)) return rc;
2024
2025 switch(property)
2026 {
2027 case CPUPropertyType_PAE:
2028 setModified(IsModified_MachineData);
2029 mHWData.backup();
2030 mHWData->mPAEEnabled = !!aVal;
2031 break;
2032
2033 case CPUPropertyType_Synthetic:
2034 setModified(IsModified_MachineData);
2035 mHWData.backup();
2036 mHWData->mSyntheticCpu = !!aVal;
2037 break;
2038
2039 default:
2040 return E_INVALIDARG;
2041 }
2042 return S_OK;
2043}
2044
2045STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2046{
2047 CheckComArgOutPointerValid(aValEax);
2048 CheckComArgOutPointerValid(aValEbx);
2049 CheckComArgOutPointerValid(aValEcx);
2050 CheckComArgOutPointerValid(aValEdx);
2051
2052 AutoCaller autoCaller(this);
2053 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2054
2055 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2056
2057 switch(aId)
2058 {
2059 case 0x0:
2060 case 0x1:
2061 case 0x2:
2062 case 0x3:
2063 case 0x4:
2064 case 0x5:
2065 case 0x6:
2066 case 0x7:
2067 case 0x8:
2068 case 0x9:
2069 case 0xA:
2070 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2071 return E_INVALIDARG;
2072
2073 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2074 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2075 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2076 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2077 break;
2078
2079 case 0x80000000:
2080 case 0x80000001:
2081 case 0x80000002:
2082 case 0x80000003:
2083 case 0x80000004:
2084 case 0x80000005:
2085 case 0x80000006:
2086 case 0x80000007:
2087 case 0x80000008:
2088 case 0x80000009:
2089 case 0x8000000A:
2090 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2091 return E_INVALIDARG;
2092
2093 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2094 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2095 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2096 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2097 break;
2098
2099 default:
2100 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2101 }
2102 return S_OK;
2103}
2104
2105STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2106{
2107 AutoCaller autoCaller(this);
2108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2109
2110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2111
2112 HRESULT rc = checkStateDependency(MutableStateDep);
2113 if (FAILED(rc)) return rc;
2114
2115 switch(aId)
2116 {
2117 case 0x0:
2118 case 0x1:
2119 case 0x2:
2120 case 0x3:
2121 case 0x4:
2122 case 0x5:
2123 case 0x6:
2124 case 0x7:
2125 case 0x8:
2126 case 0x9:
2127 case 0xA:
2128 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2129 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2130 setModified(IsModified_MachineData);
2131 mHWData.backup();
2132 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2133 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2134 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2135 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2136 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2137 break;
2138
2139 case 0x80000000:
2140 case 0x80000001:
2141 case 0x80000002:
2142 case 0x80000003:
2143 case 0x80000004:
2144 case 0x80000005:
2145 case 0x80000006:
2146 case 0x80000007:
2147 case 0x80000008:
2148 case 0x80000009:
2149 case 0x8000000A:
2150 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2151 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2152 setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2155 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2156 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2157 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2158 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2159 break;
2160
2161 default:
2162 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2163 }
2164 return S_OK;
2165}
2166
2167STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2168{
2169 AutoCaller autoCaller(this);
2170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2171
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 switch(aId)
2178 {
2179 case 0x0:
2180 case 0x1:
2181 case 0x2:
2182 case 0x3:
2183 case 0x4:
2184 case 0x5:
2185 case 0x6:
2186 case 0x7:
2187 case 0x8:
2188 case 0x9:
2189 case 0xA:
2190 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2191 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2192 setModified(IsModified_MachineData);
2193 mHWData.backup();
2194 /* Invalidate leaf. */
2195 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2196 break;
2197
2198 case 0x80000000:
2199 case 0x80000001:
2200 case 0x80000002:
2201 case 0x80000003:
2202 case 0x80000004:
2203 case 0x80000005:
2204 case 0x80000006:
2205 case 0x80000007:
2206 case 0x80000008:
2207 case 0x80000009:
2208 case 0x8000000A:
2209 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2210 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2211 setModified(IsModified_MachineData);
2212 mHWData.backup();
2213 /* Invalidate leaf. */
2214 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2215 break;
2216
2217 default:
2218 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2219 }
2220 return S_OK;
2221}
2222
2223STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2224{
2225 AutoCaller autoCaller(this);
2226 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2227
2228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2229
2230 HRESULT rc = checkStateDependency(MutableStateDep);
2231 if (FAILED(rc)) return rc;
2232
2233 setModified(IsModified_MachineData);
2234 mHWData.backup();
2235
2236 /* Invalidate all standard leafs. */
2237 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2238 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2239
2240 /* Invalidate all extended leafs. */
2241 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2242 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2243
2244 return S_OK;
2245}
2246
2247STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2248{
2249 CheckComArgOutPointerValid(aVal);
2250
2251 AutoCaller autoCaller(this);
2252 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2253
2254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2255
2256 switch(property)
2257 {
2258 case HWVirtExPropertyType_Enabled:
2259 *aVal = mHWData->mHWVirtExEnabled;
2260 break;
2261
2262 case HWVirtExPropertyType_Exclusive:
2263 *aVal = mHWData->mHWVirtExExclusive;
2264 break;
2265
2266 case HWVirtExPropertyType_VPID:
2267 *aVal = mHWData->mHWVirtExVPIDEnabled;
2268 break;
2269
2270 case HWVirtExPropertyType_NestedPaging:
2271 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2272 break;
2273
2274 case HWVirtExPropertyType_LargePages:
2275 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2276#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2277 *aVal = FALSE;
2278#endif
2279 break;
2280
2281 case HWVirtExPropertyType_Force:
2282 *aVal = mHWData->mHWVirtExForceEnabled;
2283 break;
2284
2285 default:
2286 return E_INVALIDARG;
2287 }
2288 return S_OK;
2289}
2290
2291STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2292{
2293 AutoCaller autoCaller(this);
2294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2295
2296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2297
2298 HRESULT rc = checkStateDependency(MutableStateDep);
2299 if (FAILED(rc)) return rc;
2300
2301 switch(property)
2302 {
2303 case HWVirtExPropertyType_Enabled:
2304 setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mHWVirtExEnabled = !!aVal;
2307 break;
2308
2309 case HWVirtExPropertyType_Exclusive:
2310 setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mHWVirtExExclusive = !!aVal;
2313 break;
2314
2315 case HWVirtExPropertyType_VPID:
2316 setModified(IsModified_MachineData);
2317 mHWData.backup();
2318 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2319 break;
2320
2321 case HWVirtExPropertyType_NestedPaging:
2322 setModified(IsModified_MachineData);
2323 mHWData.backup();
2324 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2325 break;
2326
2327 case HWVirtExPropertyType_LargePages:
2328 setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2331 break;
2332
2333 case HWVirtExPropertyType_Force:
2334 setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mHWVirtExForceEnabled = !!aVal;
2337 break;
2338
2339 default:
2340 return E_INVALIDARG;
2341 }
2342
2343 return S_OK;
2344}
2345
2346STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2347{
2348 CheckComArgOutPointerValid(aSnapshotFolder);
2349
2350 AutoCaller autoCaller(this);
2351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2352
2353 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2354
2355 Utf8Str strFullSnapshotFolder;
2356 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2357 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2358
2359 return S_OK;
2360}
2361
2362STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2363{
2364 /* @todo (r=dmik):
2365 * 1. Allow to change the name of the snapshot folder containing snapshots
2366 * 2. Rename the folder on disk instead of just changing the property
2367 * value (to be smart and not to leave garbage). Note that it cannot be
2368 * done here because the change may be rolled back. Thus, the right
2369 * place is #saveSettings().
2370 */
2371
2372 AutoCaller autoCaller(this);
2373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2374
2375 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2376
2377 HRESULT rc = checkStateDependency(MutableStateDep);
2378 if (FAILED(rc)) return rc;
2379
2380 if (!mData->mCurrentSnapshot.isNull())
2381 return setError(E_FAIL,
2382 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2383
2384 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2385
2386 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2387 if (strSnapshotFolder.isEmpty())
2388 strSnapshotFolder = "Snapshots";
2389 int vrc = calculateFullPath(strSnapshotFolder,
2390 strSnapshotFolder);
2391 if (RT_FAILURE(vrc))
2392 return setError(E_FAIL,
2393 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2394 aSnapshotFolder, vrc);
2395
2396 setModified(IsModified_MachineData);
2397 mUserData.backup();
2398
2399 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2400
2401 return S_OK;
2402}
2403
2404STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2405{
2406 CheckComArgOutSafeArrayPointerValid(aAttachments);
2407
2408 AutoCaller autoCaller(this);
2409 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2410
2411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2414 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2415
2416 return S_OK;
2417}
2418
2419STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2420{
2421 CheckComArgOutPointerValid(vrdeServer);
2422
2423 AutoCaller autoCaller(this);
2424 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2425
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 Assert(!!mVRDEServer);
2429 mVRDEServer.queryInterfaceTo(vrdeServer);
2430
2431 return S_OK;
2432}
2433
2434STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2435{
2436 CheckComArgOutPointerValid(audioAdapter);
2437
2438 AutoCaller autoCaller(this);
2439 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2440
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 mAudioAdapter.queryInterfaceTo(audioAdapter);
2444 return S_OK;
2445}
2446
2447STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2448{
2449#ifdef VBOX_WITH_VUSB
2450 CheckComArgOutPointerValid(aUSBController);
2451
2452 AutoCaller autoCaller(this);
2453 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2454
2455 clearError();
2456 MultiResult rc(S_OK);
2457
2458# ifdef VBOX_WITH_USB
2459 rc = mParent->host()->checkUSBProxyService();
2460 if (FAILED(rc)) return rc;
2461# endif
2462
2463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 return rc = mUSBController.queryInterfaceTo(aUSBController);
2466#else
2467 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2468 * extended error info to indicate that USB is simply not available
2469 * (w/o treating it as a failure), for example, as in OSE */
2470 NOREF(aUSBController);
2471 ReturnComNotImplemented();
2472#endif /* VBOX_WITH_VUSB */
2473}
2474
2475STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2476{
2477 CheckComArgOutPointerValid(aFilePath);
2478
2479 AutoLimitedCaller autoCaller(this);
2480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2481
2482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 mData->m_strConfigFileFull.cloneTo(aFilePath);
2485 return S_OK;
2486}
2487
2488STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2489{
2490 CheckComArgOutPointerValid(aModified);
2491
2492 AutoCaller autoCaller(this);
2493 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2494
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 HRESULT rc = checkStateDependency(MutableStateDep);
2498 if (FAILED(rc)) return rc;
2499
2500 if (!mData->pMachineConfigFile->fileExists())
2501 // this is a new machine, and no config file exists yet:
2502 *aModified = TRUE;
2503 else
2504 *aModified = (mData->flModifications != 0);
2505
2506 return S_OK;
2507}
2508
2509STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2510{
2511 CheckComArgOutPointerValid(aSessionState);
2512
2513 AutoCaller autoCaller(this);
2514 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 *aSessionState = mData->mSession.mState;
2519
2520 return S_OK;
2521}
2522
2523STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2524{
2525 CheckComArgOutPointerValid(aSessionType);
2526
2527 AutoCaller autoCaller(this);
2528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2529
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 mData->mSession.mType.cloneTo(aSessionType);
2533
2534 return S_OK;
2535}
2536
2537STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2538{
2539 CheckComArgOutPointerValid(aSessionPID);
2540
2541 AutoCaller autoCaller(this);
2542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2543
2544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2545
2546 *aSessionPID = mData->mSession.mPID;
2547
2548 return S_OK;
2549}
2550
2551STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2552{
2553 CheckComArgOutPointerValid(machineState);
2554
2555 AutoCaller autoCaller(this);
2556 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2557
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 *machineState = mData->mMachineState;
2561
2562 return S_OK;
2563}
2564
2565STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2566{
2567 CheckComArgOutPointerValid(aLastStateChange);
2568
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2575
2576 return S_OK;
2577}
2578
2579STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2580{
2581 CheckComArgOutPointerValid(aStateFilePath);
2582
2583 AutoCaller autoCaller(this);
2584 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2585
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2589
2590 return S_OK;
2591}
2592
2593STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2594{
2595 CheckComArgOutPointerValid(aLogFolder);
2596
2597 AutoCaller autoCaller(this);
2598 AssertComRCReturnRC(autoCaller.rc());
2599
2600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2601
2602 Utf8Str logFolder;
2603 getLogFolder(logFolder);
2604 logFolder.cloneTo(aLogFolder);
2605
2606 return S_OK;
2607}
2608
2609STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2610{
2611 CheckComArgOutPointerValid(aCurrentSnapshot);
2612
2613 AutoCaller autoCaller(this);
2614 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2615
2616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2617
2618 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2619
2620 return S_OK;
2621}
2622
2623STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2624{
2625 CheckComArgOutPointerValid(aSnapshotCount);
2626
2627 AutoCaller autoCaller(this);
2628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2629
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2633 ? 0
2634 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2635
2636 return S_OK;
2637}
2638
2639STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2640{
2641 CheckComArgOutPointerValid(aCurrentStateModified);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 /* Note: for machines with no snapshots, we always return FALSE
2649 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2650 * reasons :) */
2651
2652 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2653 ? FALSE
2654 : mData->mCurrentStateModified;
2655
2656 return S_OK;
2657}
2658
2659STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2660{
2661 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2662
2663 AutoCaller autoCaller(this);
2664 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2665
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2669 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2670
2671 return S_OK;
2672}
2673
2674STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2675{
2676 CheckComArgOutPointerValid(aClipboardMode);
2677
2678 AutoCaller autoCaller(this);
2679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2680
2681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2682
2683 *aClipboardMode = mHWData->mClipboardMode;
2684
2685 return S_OK;
2686}
2687
2688STDMETHODIMP
2689Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2690{
2691 HRESULT rc = S_OK;
2692
2693 AutoCaller autoCaller(this);
2694 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2695
2696 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2697
2698 alock.release();
2699 rc = onClipboardModeChange(aClipboardMode);
2700 alock.acquire();
2701 if (FAILED(rc)) return rc;
2702
2703 setModified(IsModified_MachineData);
2704 mHWData.backup();
2705 mHWData->mClipboardMode = aClipboardMode;
2706
2707 /* Save settings if online - todo why is this required?? */
2708 if (Global::IsOnline(mData->mMachineState))
2709 saveSettings(NULL);
2710
2711 return S_OK;
2712}
2713
2714STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2715{
2716 CheckComArgOutPointerValid(aDragAndDropMode);
2717
2718 AutoCaller autoCaller(this);
2719 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2720
2721 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2722
2723 *aDragAndDropMode = mHWData->mDragAndDropMode;
2724
2725 return S_OK;
2726}
2727
2728STDMETHODIMP
2729Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2730{
2731 HRESULT rc = S_OK;
2732
2733 AutoCaller autoCaller(this);
2734 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2735
2736 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 alock.release();
2739 rc = onDragAndDropModeChange(aDragAndDropMode);
2740 alock.acquire();
2741 if (FAILED(rc)) return rc;
2742
2743 setModified(IsModified_MachineData);
2744 mHWData.backup();
2745 mHWData->mDragAndDropMode = aDragAndDropMode;
2746
2747 /* Save settings if online - todo why is this required?? */
2748 if (Global::IsOnline(mData->mMachineState))
2749 saveSettings(NULL);
2750
2751 return S_OK;
2752}
2753
2754STDMETHODIMP
2755Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2756{
2757 CheckComArgOutPointerValid(aPatterns);
2758
2759 AutoCaller autoCaller(this);
2760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2761
2762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2763
2764 try
2765 {
2766 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2767 }
2768 catch (...)
2769 {
2770 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2771 }
2772
2773 return S_OK;
2774}
2775
2776STDMETHODIMP
2777Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2778{
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 HRESULT rc = checkStateDependency(MutableStateDep);
2785 if (FAILED(rc)) return rc;
2786
2787 setModified(IsModified_MachineData);
2788 mHWData.backup();
2789 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2790 return rc;
2791}
2792
2793STDMETHODIMP
2794Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2795{
2796 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2797
2798 AutoCaller autoCaller(this);
2799 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2800
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2804 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2805
2806 return S_OK;
2807}
2808
2809STDMETHODIMP
2810Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2811{
2812 CheckComArgOutPointerValid(aEnabled);
2813
2814 AutoCaller autoCaller(this);
2815 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2816
2817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2818
2819 *aEnabled = mUserData->s.fTeleporterEnabled;
2820
2821 return S_OK;
2822}
2823
2824STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2825{
2826 AutoCaller autoCaller(this);
2827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2828
2829 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2830
2831 /* Only allow it to be set to true when PoweredOff or Aborted.
2832 (Clearing it is always permitted.) */
2833 if ( aEnabled
2834 && mData->mRegistered
2835 && ( !isSessionMachine()
2836 || ( mData->mMachineState != MachineState_PoweredOff
2837 && mData->mMachineState != MachineState_Teleported
2838 && mData->mMachineState != MachineState_Aborted
2839 )
2840 )
2841 )
2842 return setError(VBOX_E_INVALID_VM_STATE,
2843 tr("The machine is not powered off (state is %s)"),
2844 Global::stringifyMachineState(mData->mMachineState));
2845
2846 setModified(IsModified_MachineData);
2847 mUserData.backup();
2848 mUserData->s.fTeleporterEnabled = !!aEnabled;
2849
2850 return S_OK;
2851}
2852
2853STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2854{
2855 CheckComArgOutPointerValid(aPort);
2856
2857 AutoCaller autoCaller(this);
2858 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2859
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2863
2864 return S_OK;
2865}
2866
2867STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2868{
2869 if (aPort >= _64K)
2870 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2871
2872 AutoCaller autoCaller(this);
2873 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2874
2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 HRESULT rc = checkStateDependency(MutableStateDep);
2878 if (FAILED(rc)) return rc;
2879
2880 setModified(IsModified_MachineData);
2881 mUserData.backup();
2882 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2883
2884 return S_OK;
2885}
2886
2887STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2888{
2889 CheckComArgOutPointerValid(aAddress);
2890
2891 AutoCaller autoCaller(this);
2892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2893
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2897
2898 return S_OK;
2899}
2900
2901STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2902{
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.strTeleporterAddress = aAddress;
2914
2915 return S_OK;
2916}
2917
2918STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2919{
2920 CheckComArgOutPointerValid(aPassword);
2921
2922 AutoCaller autoCaller(this);
2923 HRESULT hrc = autoCaller.rc();
2924 if (SUCCEEDED(hrc))
2925 {
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
2928 }
2929
2930 return hrc;
2931}
2932
2933STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
2934{
2935 /*
2936 * Hash the password first.
2937 */
2938 Utf8Str strPassword(aPassword);
2939 if (!strPassword.isEmpty())
2940 {
2941 if (VBoxIsPasswordHashed(&strPassword))
2942 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2943 VBoxHashPassword(&strPassword);
2944 }
2945
2946 /*
2947 * Do the update.
2948 */
2949 AutoCaller autoCaller(this);
2950 HRESULT hrc = autoCaller.rc();
2951 if (SUCCEEDED(hrc))
2952 {
2953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2954 hrc = checkStateDependency(MutableStateDep);
2955 if (SUCCEEDED(hrc))
2956 {
2957 setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.strTeleporterPassword = strPassword;
2960 }
2961 }
2962
2963 return hrc;
2964}
2965
2966STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
2967{
2968 CheckComArgOutPointerValid(aState);
2969
2970 AutoCaller autoCaller(this);
2971 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2972
2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 *aState = mUserData->s.enmFaultToleranceState;
2976 return S_OK;
2977}
2978
2979STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
2980{
2981 AutoCaller autoCaller(this);
2982 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2983
2984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2985
2986 /* @todo deal with running state change. */
2987 HRESULT rc = checkStateDependency(MutableStateDep);
2988 if (FAILED(rc)) return rc;
2989
2990 setModified(IsModified_MachineData);
2991 mUserData.backup();
2992 mUserData->s.enmFaultToleranceState = aState;
2993 return S_OK;
2994}
2995
2996STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
2997{
2998 CheckComArgOutPointerValid(aAddress);
2999
3000 AutoCaller autoCaller(this);
3001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3002
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004
3005 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3006 return S_OK;
3007}
3008
3009STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3010{
3011 AutoCaller autoCaller(this);
3012 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3013
3014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 /* @todo deal with running state change. */
3017 HRESULT rc = checkStateDependency(MutableStateDep);
3018 if (FAILED(rc)) return rc;
3019
3020 setModified(IsModified_MachineData);
3021 mUserData.backup();
3022 mUserData->s.strFaultToleranceAddress = aAddress;
3023 return S_OK;
3024}
3025
3026STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3027{
3028 CheckComArgOutPointerValid(aPort);
3029
3030 AutoCaller autoCaller(this);
3031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3032
3033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3034
3035 *aPort = mUserData->s.uFaultTolerancePort;
3036 return S_OK;
3037}
3038
3039STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3040{
3041 AutoCaller autoCaller(this);
3042 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3043
3044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 /* @todo deal with running state change. */
3047 HRESULT rc = checkStateDependency(MutableStateDep);
3048 if (FAILED(rc)) return rc;
3049
3050 setModified(IsModified_MachineData);
3051 mUserData.backup();
3052 mUserData->s.uFaultTolerancePort = aPort;
3053 return S_OK;
3054}
3055
3056STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3057{
3058 CheckComArgOutPointerValid(aPassword);
3059
3060 AutoCaller autoCaller(this);
3061 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3062
3063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3066
3067 return S_OK;
3068}
3069
3070STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3071{
3072 AutoCaller autoCaller(this);
3073 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3074
3075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3076
3077 /* @todo deal with running state change. */
3078 HRESULT rc = checkStateDependency(MutableStateDep);
3079 if (FAILED(rc)) return rc;
3080
3081 setModified(IsModified_MachineData);
3082 mUserData.backup();
3083 mUserData->s.strFaultTolerancePassword = aPassword;
3084
3085 return S_OK;
3086}
3087
3088STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3089{
3090 CheckComArgOutPointerValid(aInterval);
3091
3092 AutoCaller autoCaller(this);
3093 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3094
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096
3097 *aInterval = mUserData->s.uFaultToleranceInterval;
3098 return S_OK;
3099}
3100
3101STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3102{
3103 AutoCaller autoCaller(this);
3104 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3105
3106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 /* @todo deal with running state change. */
3109 HRESULT rc = checkStateDependency(MutableStateDep);
3110 if (FAILED(rc)) return rc;
3111
3112 setModified(IsModified_MachineData);
3113 mUserData.backup();
3114 mUserData->s.uFaultToleranceInterval = aInterval;
3115 return S_OK;
3116}
3117
3118STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3119{
3120 CheckComArgOutPointerValid(aEnabled);
3121
3122 AutoCaller autoCaller(this);
3123 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3124
3125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 *aEnabled = mUserData->s.fRTCUseUTC;
3128
3129 return S_OK;
3130}
3131
3132STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3133{
3134 AutoCaller autoCaller(this);
3135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3136
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /* Only allow it to be set to true when PoweredOff or Aborted.
3140 (Clearing it is always permitted.) */
3141 if ( aEnabled
3142 && mData->mRegistered
3143 && ( !isSessionMachine()
3144 || ( mData->mMachineState != MachineState_PoweredOff
3145 && mData->mMachineState != MachineState_Teleported
3146 && mData->mMachineState != MachineState_Aborted
3147 )
3148 )
3149 )
3150 return setError(VBOX_E_INVALID_VM_STATE,
3151 tr("The machine is not powered off (state is %s)"),
3152 Global::stringifyMachineState(mData->mMachineState));
3153
3154 setModified(IsModified_MachineData);
3155 mUserData.backup();
3156 mUserData->s.fRTCUseUTC = !!aEnabled;
3157
3158 return S_OK;
3159}
3160
3161STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3162{
3163 CheckComArgOutPointerValid(aEnabled);
3164
3165 AutoCaller autoCaller(this);
3166 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3167
3168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3169
3170 *aEnabled = mHWData->mIOCacheEnabled;
3171
3172 return S_OK;
3173}
3174
3175STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3176{
3177 AutoCaller autoCaller(this);
3178 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3179
3180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 HRESULT rc = checkStateDependency(MutableStateDep);
3183 if (FAILED(rc)) return rc;
3184
3185 setModified(IsModified_MachineData);
3186 mHWData.backup();
3187 mHWData->mIOCacheEnabled = aEnabled;
3188
3189 return S_OK;
3190}
3191
3192STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3193{
3194 CheckComArgOutPointerValid(aIOCacheSize);
3195
3196 AutoCaller autoCaller(this);
3197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3198
3199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3200
3201 *aIOCacheSize = mHWData->mIOCacheSize;
3202
3203 return S_OK;
3204}
3205
3206STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3207{
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 HRESULT rc = checkStateDependency(MutableStateDep);
3214 if (FAILED(rc)) return rc;
3215
3216 setModified(IsModified_MachineData);
3217 mHWData.backup();
3218 mHWData->mIOCacheSize = aIOCacheSize;
3219
3220 return S_OK;
3221}
3222
3223
3224/**
3225 * @note Locks objects!
3226 */
3227STDMETHODIMP Machine::LockMachine(ISession *aSession,
3228 LockType_T lockType)
3229{
3230 CheckComArgNotNull(aSession);
3231
3232 AutoCaller autoCaller(this);
3233 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3234
3235 /* check the session state */
3236 SessionState_T state;
3237 HRESULT rc = aSession->COMGETTER(State)(&state);
3238 if (FAILED(rc)) return rc;
3239
3240 if (state != SessionState_Unlocked)
3241 return setError(VBOX_E_INVALID_OBJECT_STATE,
3242 tr("The given session is busy"));
3243
3244 // get the client's IInternalSessionControl interface
3245 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3246 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3247 E_INVALIDARG);
3248
3249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3250
3251 if (!mData->mRegistered)
3252 return setError(E_UNEXPECTED,
3253 tr("The machine '%s' is not registered"),
3254 mUserData->s.strName.c_str());
3255
3256 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3257
3258 SessionState_T oldState = mData->mSession.mState;
3259 /* Hack: in case the session is closing and there is a progress object
3260 * which allows waiting for the session to be closed, take the opportunity
3261 * and do a limited wait (max. 1 second). This helps a lot when the system
3262 * is busy and thus session closing can take a little while. */
3263 if ( mData->mSession.mState == SessionState_Unlocking
3264 && mData->mSession.mProgress)
3265 {
3266 alock.release();
3267 mData->mSession.mProgress->WaitForCompletion(1000);
3268 alock.acquire();
3269 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3270 }
3271
3272 // try again now
3273 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3274 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3275 )
3276 {
3277 // OK, share the session... we are now dealing with three processes:
3278 // 1) VBoxSVC (where this code runs);
3279 // 2) process C: the caller's client process (who wants a shared session);
3280 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3281
3282 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3283 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3284 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3285 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3286 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3287
3288 /*
3289 * Release the lock before calling the client process. It's safe here
3290 * since the only thing to do after we get the lock again is to add
3291 * the remote control to the list (which doesn't directly influence
3292 * anything).
3293 */
3294 alock.release();
3295
3296 // get the console of the session holding the write lock (this is a remote call)
3297 ComPtr<IConsole> pConsoleW;
3298 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3299 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3300 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3301 if (FAILED(rc))
3302 // the failure may occur w/o any error info (from RPC), so provide one
3303 return setError(VBOX_E_VM_ERROR,
3304 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3305
3306 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3307
3308 // share the session machine and W's console with the caller's session
3309 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3310 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3311 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3312
3313 if (FAILED(rc))
3314 // the failure may occur w/o any error info (from RPC), so provide one
3315 return setError(VBOX_E_VM_ERROR,
3316 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3317 alock.acquire();
3318
3319 // need to revalidate the state after acquiring the lock again
3320 if (mData->mSession.mState != SessionState_Locked)
3321 {
3322 pSessionControl->Uninitialize();
3323 return setError(VBOX_E_INVALID_SESSION_STATE,
3324 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3325 mUserData->s.strName.c_str());
3326 }
3327
3328 // add the caller's session to the list
3329 mData->mSession.mRemoteControls.push_back(pSessionControl);
3330 }
3331 else if ( mData->mSession.mState == SessionState_Locked
3332 || mData->mSession.mState == SessionState_Unlocking
3333 )
3334 {
3335 // sharing not permitted, or machine still unlocking:
3336 return setError(VBOX_E_INVALID_OBJECT_STATE,
3337 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3338 mUserData->s.strName.c_str());
3339 }
3340 else
3341 {
3342 // machine is not locked: then write-lock the machine (create the session machine)
3343
3344 // must not be busy
3345 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3346
3347 // get the caller's session PID
3348 RTPROCESS pid = NIL_RTPROCESS;
3349 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3350 pSessionControl->GetPID((ULONG*)&pid);
3351 Assert(pid != NIL_RTPROCESS);
3352
3353 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3354
3355 if (fLaunchingVMProcess)
3356 {
3357 // this machine is awaiting for a spawning session to be opened:
3358 // then the calling process must be the one that got started by
3359 // LaunchVMProcess()
3360
3361 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3362 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3363
3364 if (mData->mSession.mPID != pid)
3365 return setError(E_ACCESSDENIED,
3366 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3367 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3368 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3369 }
3370
3371 // create the mutable SessionMachine from the current machine
3372 ComObjPtr<SessionMachine> sessionMachine;
3373 sessionMachine.createObject();
3374 rc = sessionMachine->init(this);
3375 AssertComRC(rc);
3376
3377 /* NOTE: doing return from this function after this point but
3378 * before the end is forbidden since it may call SessionMachine::uninit()
3379 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3380 * lock while still holding the Machine lock in alock so that a deadlock
3381 * is possible due to the wrong lock order. */
3382
3383 if (SUCCEEDED(rc))
3384 {
3385 /*
3386 * Set the session state to Spawning to protect against subsequent
3387 * attempts to open a session and to unregister the machine after
3388 * we release the lock.
3389 */
3390 SessionState_T origState = mData->mSession.mState;
3391 mData->mSession.mState = SessionState_Spawning;
3392
3393 /*
3394 * Release the lock before calling the client process -- it will call
3395 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3396 * because the state is Spawning, so that LaunchVMProcess() and
3397 * LockMachine() calls will fail. This method, called before we
3398 * acquire the lock again, will fail because of the wrong PID.
3399 *
3400 * Note that mData->mSession.mRemoteControls accessed outside
3401 * the lock may not be modified when state is Spawning, so it's safe.
3402 */
3403 alock.release();
3404
3405 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3406 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3407 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3408
3409 /* The failure may occur w/o any error info (from RPC), so provide one */
3410 if (FAILED(rc))
3411 setError(VBOX_E_VM_ERROR,
3412 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3413
3414 if ( SUCCEEDED(rc)
3415 && fLaunchingVMProcess
3416 )
3417 {
3418 /* complete the remote session initialization */
3419
3420 /* get the console from the direct session */
3421 ComPtr<IConsole> console;
3422 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3423 ComAssertComRC(rc);
3424
3425 if (SUCCEEDED(rc) && !console)
3426 {
3427 ComAssert(!!console);
3428 rc = E_FAIL;
3429 }
3430
3431 /* assign machine & console to the remote session */
3432 if (SUCCEEDED(rc))
3433 {
3434 /*
3435 * after LaunchVMProcess(), the first and the only
3436 * entry in remoteControls is that remote session
3437 */
3438 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3439 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3440 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3441
3442 /* The failure may occur w/o any error info (from RPC), so provide one */
3443 if (FAILED(rc))
3444 setError(VBOX_E_VM_ERROR,
3445 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3446 }
3447
3448 if (FAILED(rc))
3449 pSessionControl->Uninitialize();
3450 }
3451
3452 /* acquire the lock again */
3453 alock.acquire();
3454
3455 /* Restore the session state */
3456 mData->mSession.mState = origState;
3457 }
3458
3459 // finalize spawning anyway (this is why we don't return on errors above)
3460 if (fLaunchingVMProcess)
3461 {
3462 /* Note that the progress object is finalized later */
3463 /** @todo Consider checking mData->mSession.mProgress for cancellation
3464 * around here. */
3465
3466 /* We don't reset mSession.mPID here because it is necessary for
3467 * SessionMachine::uninit() to reap the child process later. */
3468
3469 if (FAILED(rc))
3470 {
3471 /* Close the remote session, remove the remote control from the list
3472 * and reset session state to Closed (@note keep the code in sync
3473 * with the relevant part in openSession()). */
3474
3475 Assert(mData->mSession.mRemoteControls.size() == 1);
3476 if (mData->mSession.mRemoteControls.size() == 1)
3477 {
3478 ErrorInfoKeeper eik;
3479 mData->mSession.mRemoteControls.front()->Uninitialize();
3480 }
3481
3482 mData->mSession.mRemoteControls.clear();
3483 mData->mSession.mState = SessionState_Unlocked;
3484 }
3485 }
3486 else
3487 {
3488 /* memorize PID of the directly opened session */
3489 if (SUCCEEDED(rc))
3490 mData->mSession.mPID = pid;
3491 }
3492
3493 if (SUCCEEDED(rc))
3494 {
3495 /* memorize the direct session control and cache IUnknown for it */
3496 mData->mSession.mDirectControl = pSessionControl;
3497 mData->mSession.mState = SessionState_Locked;
3498 /* associate the SessionMachine with this Machine */
3499 mData->mSession.mMachine = sessionMachine;
3500
3501 /* request an IUnknown pointer early from the remote party for later
3502 * identity checks (it will be internally cached within mDirectControl
3503 * at least on XPCOM) */
3504 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3505 NOREF(unk);
3506 }
3507
3508 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3509 * would break the lock order */
3510 alock.release();
3511
3512 /* uninitialize the created session machine on failure */
3513 if (FAILED(rc))
3514 sessionMachine->uninit();
3515
3516 }
3517
3518 if (SUCCEEDED(rc))
3519 {
3520 /*
3521 * tell the client watcher thread to update the set of
3522 * machines that have open sessions
3523 */
3524 mParent->updateClientWatcher();
3525
3526 if (oldState != SessionState_Locked)
3527 /* fire an event */
3528 mParent->onSessionStateChange(getId(), SessionState_Locked);
3529 }
3530
3531 return rc;
3532}
3533
3534/**
3535 * @note Locks objects!
3536 */
3537STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3538 IN_BSTR aType,
3539 IN_BSTR aEnvironment,
3540 IProgress **aProgress)
3541{
3542 CheckComArgStrNotEmptyOrNull(aType);
3543 Utf8Str strType(aType);
3544 Utf8Str strEnvironment(aEnvironment);
3545 /* "emergencystop" doesn't need the session, so skip the checks/interface
3546 * retrieval. This code doesn't quite fit in here, but introducing a
3547 * special API method would be even more effort, and would require explicit
3548 * support by every API client. It's better to hide the feature a bit. */
3549 if (strType != "emergencystop")
3550 CheckComArgNotNull(aSession);
3551 CheckComArgOutPointerValid(aProgress);
3552
3553 AutoCaller autoCaller(this);
3554 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3555
3556 ComPtr<IInternalSessionControl> control;
3557 HRESULT rc = S_OK;
3558
3559 if (strType != "emergencystop")
3560 {
3561 /* check the session state */
3562 SessionState_T state;
3563 rc = aSession->COMGETTER(State)(&state);
3564 if (FAILED(rc))
3565 return rc;
3566
3567 if (state != SessionState_Unlocked)
3568 return setError(VBOX_E_INVALID_OBJECT_STATE,
3569 tr("The given session is busy"));
3570
3571 /* get the IInternalSessionControl interface */
3572 control = aSession;
3573 ComAssertMsgRet(!control.isNull(),
3574 ("No IInternalSessionControl interface"),
3575 E_INVALIDARG);
3576 }
3577
3578 /* get the teleporter enable state for the progress object init. */
3579 BOOL fTeleporterEnabled;
3580 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3581 if (FAILED(rc))
3582 return rc;
3583
3584 /* create a progress object */
3585 if (strType != "emergencystop")
3586 {
3587 ComObjPtr<ProgressProxy> progress;
3588 progress.createObject();
3589 rc = progress->init(mParent,
3590 static_cast<IMachine*>(this),
3591 Bstr(tr("Starting VM")).raw(),
3592 TRUE /* aCancelable */,
3593 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3594 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(),
3595 2 /* uFirstOperationWeight */,
3596 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3597
3598 if (SUCCEEDED(rc))
3599 {
3600 rc = launchVMProcess(control, strType, strEnvironment, progress);
3601 if (SUCCEEDED(rc))
3602 {
3603 progress.queryInterfaceTo(aProgress);
3604
3605 /* signal the client watcher thread */
3606 mParent->updateClientWatcher();
3607
3608 /* fire an event */
3609 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3610 }
3611 }
3612 }
3613 else
3614 {
3615 /* no progress object - either instant success or failure */
3616 *aProgress = NULL;
3617
3618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3619
3620 if (mData->mSession.mState != SessionState_Locked)
3621 return setError(VBOX_E_INVALID_OBJECT_STATE,
3622 tr("The machine '%s' is not locked by a session"),
3623 mUserData->s.strName.c_str());
3624
3625 /* must have a VM process associated - do not kill normal API clients
3626 * with an open session */
3627 if (!Global::IsOnline(mData->mMachineState))
3628 return setError(VBOX_E_INVALID_OBJECT_STATE,
3629 tr("The machine '%s' does not have a VM process"),
3630 mUserData->s.strName.c_str());
3631
3632 /* forcibly terminate the VM process */
3633 if (mData->mSession.mPID != NIL_RTPROCESS)
3634 RTProcTerminate(mData->mSession.mPID);
3635
3636 /* signal the client watcher thread, as most likely the client has
3637 * been terminated */
3638 mParent->updateClientWatcher();
3639 }
3640
3641 return rc;
3642}
3643
3644STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3645{
3646 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3647 return setError(E_INVALIDARG,
3648 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3649 aPosition, SchemaDefs::MaxBootPosition);
3650
3651 if (aDevice == DeviceType_USB)
3652 return setError(E_NOTIMPL,
3653 tr("Booting from USB device is currently not supported"));
3654
3655 AutoCaller autoCaller(this);
3656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3657
3658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3659
3660 HRESULT rc = checkStateDependency(MutableStateDep);
3661 if (FAILED(rc)) return rc;
3662
3663 setModified(IsModified_MachineData);
3664 mHWData.backup();
3665 mHWData->mBootOrder[aPosition - 1] = aDevice;
3666
3667 return S_OK;
3668}
3669
3670STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3671{
3672 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3673 return setError(E_INVALIDARG,
3674 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3675 aPosition, SchemaDefs::MaxBootPosition);
3676
3677 AutoCaller autoCaller(this);
3678 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3679
3680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3681
3682 *aDevice = mHWData->mBootOrder[aPosition - 1];
3683
3684 return S_OK;
3685}
3686
3687STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3688 LONG aControllerPort,
3689 LONG aDevice,
3690 DeviceType_T aType,
3691 IMedium *aMedium)
3692{
3693 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3694 aControllerName, aControllerPort, aDevice, aType, aMedium));
3695
3696 CheckComArgStrNotEmptyOrNull(aControllerName);
3697
3698 AutoCaller autoCaller(this);
3699 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3700
3701 // request the host lock first, since might be calling Host methods for getting host drives;
3702 // next, protect the media tree all the while we're in here, as well as our member variables
3703 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3704 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3705
3706 HRESULT rc = checkStateDependency(MutableStateDep);
3707 if (FAILED(rc)) return rc;
3708
3709 /// @todo NEWMEDIA implicit machine registration
3710 if (!mData->mRegistered)
3711 return setError(VBOX_E_INVALID_OBJECT_STATE,
3712 tr("Cannot attach storage devices to an unregistered machine"));
3713
3714 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3715
3716 /* Check for an existing controller. */
3717 ComObjPtr<StorageController> ctl;
3718 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3719 if (FAILED(rc)) return rc;
3720
3721 StorageControllerType_T ctrlType;
3722 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3723 if (FAILED(rc))
3724 return setError(E_FAIL,
3725 tr("Could not get type of controller '%ls'"),
3726 aControllerName);
3727
3728 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3729 bool fHotplug = false;
3730 if (Global::IsOnlineOrTransient(mData->mMachineState))
3731 fHotplug = true;
3732
3733 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3734 return setError(VBOX_E_INVALID_VM_STATE,
3735 tr("Controller '%ls' does not support hotplugging"),
3736 aControllerName);
3737
3738 // check that the port and device are not out of range
3739 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3740 if (FAILED(rc)) return rc;
3741
3742 /* check if the device slot is already busy */
3743 MediumAttachment *pAttachTemp;
3744 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3745 aControllerName,
3746 aControllerPort,
3747 aDevice)))
3748 {
3749 Medium *pMedium = pAttachTemp->getMedium();
3750 if (pMedium)
3751 {
3752 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3753 return setError(VBOX_E_OBJECT_IN_USE,
3754 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3755 pMedium->getLocationFull().c_str(),
3756 aControllerPort,
3757 aDevice,
3758 aControllerName);
3759 }
3760 else
3761 return setError(VBOX_E_OBJECT_IN_USE,
3762 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3763 aControllerPort, aDevice, aControllerName);
3764 }
3765
3766 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3767 if (aMedium && medium.isNull())
3768 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3769
3770 AutoCaller mediumCaller(medium);
3771 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3772
3773 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3774
3775 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3776 && !medium.isNull()
3777 )
3778 return setError(VBOX_E_OBJECT_IN_USE,
3779 tr("Medium '%s' is already attached to this virtual machine"),
3780 medium->getLocationFull().c_str());
3781
3782 if (!medium.isNull())
3783 {
3784 MediumType_T mtype = medium->getType();
3785 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3786 // For DVDs it's not written to the config file, so needs no global config
3787 // version bump. For floppies it's a new attribute "type", which is ignored
3788 // by older VirtualBox version, so needs no global config version bump either.
3789 // For hard disks this type is not accepted.
3790 if (mtype == MediumType_MultiAttach)
3791 {
3792 // This type is new with VirtualBox 4.0 and therefore requires settings
3793 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3794 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3795 // two reasons: The medium type is a property of the media registry tree, which
3796 // can reside in the global config file (for pre-4.0 media); we would therefore
3797 // possibly need to bump the global config version. We don't want to do that though
3798 // because that might make downgrading to pre-4.0 impossible.
3799 // As a result, we can only use these two new types if the medium is NOT in the
3800 // global registry:
3801 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3802 if ( medium->isInRegistry(uuidGlobalRegistry)
3803 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3804 )
3805 return setError(VBOX_E_INVALID_OBJECT_STATE,
3806 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3807 "to machines that were created with VirtualBox 4.0 or later"),
3808 medium->getLocationFull().c_str());
3809 }
3810 }
3811
3812 bool fIndirect = false;
3813 if (!medium.isNull())
3814 fIndirect = medium->isReadOnly();
3815 bool associate = true;
3816
3817 do
3818 {
3819 if ( aType == DeviceType_HardDisk
3820 && mMediaData.isBackedUp())
3821 {
3822 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3823
3824 /* check if the medium was attached to the VM before we started
3825 * changing attachments in which case the attachment just needs to
3826 * be restored */
3827 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3828 {
3829 AssertReturn(!fIndirect, E_FAIL);
3830
3831 /* see if it's the same bus/channel/device */
3832 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3833 {
3834 /* the simplest case: restore the whole attachment
3835 * and return, nothing else to do */
3836 mMediaData->mAttachments.push_back(pAttachTemp);
3837 return S_OK;
3838 }
3839
3840 /* bus/channel/device differ; we need a new attachment object,
3841 * but don't try to associate it again */
3842 associate = false;
3843 break;
3844 }
3845 }
3846
3847 /* go further only if the attachment is to be indirect */
3848 if (!fIndirect)
3849 break;
3850
3851 /* perform the so called smart attachment logic for indirect
3852 * attachments. Note that smart attachment is only applicable to base
3853 * hard disks. */
3854
3855 if (medium->getParent().isNull())
3856 {
3857 /* first, investigate the backup copy of the current hard disk
3858 * attachments to make it possible to re-attach existing diffs to
3859 * another device slot w/o losing their contents */
3860 if (mMediaData.isBackedUp())
3861 {
3862 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3863
3864 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3865 uint32_t foundLevel = 0;
3866
3867 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3868 it != oldAtts.end();
3869 ++it)
3870 {
3871 uint32_t level = 0;
3872 MediumAttachment *pAttach = *it;
3873 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3874 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3875 if (pMedium.isNull())
3876 continue;
3877
3878 if (pMedium->getBase(&level) == medium)
3879 {
3880 /* skip the hard disk if its currently attached (we
3881 * cannot attach the same hard disk twice) */
3882 if (findAttachment(mMediaData->mAttachments,
3883 pMedium))
3884 continue;
3885
3886 /* matched device, channel and bus (i.e. attached to the
3887 * same place) will win and immediately stop the search;
3888 * otherwise the attachment that has the youngest
3889 * descendant of medium will be used
3890 */
3891 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3892 {
3893 /* the simplest case: restore the whole attachment
3894 * and return, nothing else to do */
3895 mMediaData->mAttachments.push_back(*it);
3896 return S_OK;
3897 }
3898 else if ( foundIt == oldAtts.end()
3899 || level > foundLevel /* prefer younger */
3900 )
3901 {
3902 foundIt = it;
3903 foundLevel = level;
3904 }
3905 }
3906 }
3907
3908 if (foundIt != oldAtts.end())
3909 {
3910 /* use the previously attached hard disk */
3911 medium = (*foundIt)->getMedium();
3912 mediumCaller.attach(medium);
3913 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3914 mediumLock.attach(medium);
3915 /* not implicit, doesn't require association with this VM */
3916 fIndirect = false;
3917 associate = false;
3918 /* go right to the MediumAttachment creation */
3919 break;
3920 }
3921 }
3922
3923 /* must give up the medium lock and medium tree lock as below we
3924 * go over snapshots, which needs a lock with higher lock order. */
3925 mediumLock.release();
3926 treeLock.release();
3927
3928 /* then, search through snapshots for the best diff in the given
3929 * hard disk's chain to base the new diff on */
3930
3931 ComObjPtr<Medium> base;
3932 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3933 while (snap)
3934 {
3935 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3936
3937 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
3938
3939 MediumAttachment *pAttachFound = NULL;
3940 uint32_t foundLevel = 0;
3941
3942 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
3943 it != snapAtts.end();
3944 ++it)
3945 {
3946 MediumAttachment *pAttach = *it;
3947 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3948 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3949 if (pMedium.isNull())
3950 continue;
3951
3952 uint32_t level = 0;
3953 if (pMedium->getBase(&level) == medium)
3954 {
3955 /* matched device, channel and bus (i.e. attached to the
3956 * same place) will win and immediately stop the search;
3957 * otherwise the attachment that has the youngest
3958 * descendant of medium will be used
3959 */
3960 if ( pAttach->getDevice() == aDevice
3961 && pAttach->getPort() == aControllerPort
3962 && pAttach->getControllerName() == aControllerName
3963 )
3964 {
3965 pAttachFound = pAttach;
3966 break;
3967 }
3968 else if ( !pAttachFound
3969 || level > foundLevel /* prefer younger */
3970 )
3971 {
3972 pAttachFound = pAttach;
3973 foundLevel = level;
3974 }
3975 }
3976 }
3977
3978 if (pAttachFound)
3979 {
3980 base = pAttachFound->getMedium();
3981 break;
3982 }
3983
3984 snap = snap->getParent();
3985 }
3986
3987 /* re-lock medium tree and the medium, as we need it below */
3988 treeLock.acquire();
3989 mediumLock.acquire();
3990
3991 /* found a suitable diff, use it as a base */
3992 if (!base.isNull())
3993 {
3994 medium = base;
3995 mediumCaller.attach(medium);
3996 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3997 mediumLock.attach(medium);
3998 }
3999 }
4000
4001 Utf8Str strFullSnapshotFolder;
4002 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4003
4004 ComObjPtr<Medium> diff;
4005 diff.createObject();
4006 // store this diff in the same registry as the parent
4007 Guid uuidRegistryParent;
4008 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4009 {
4010 // parent image has no registry: this can happen if we're attaching a new immutable
4011 // image that has not yet been attached (medium then points to the base and we're
4012 // creating the diff image for the immutable, and the parent is not yet registered);
4013 // put the parent in the machine registry then
4014 mediumLock.release();
4015 treeLock.release();
4016 alock.release();
4017 addMediumToRegistry(medium);
4018 alock.acquire();
4019 treeLock.acquire();
4020 mediumLock.acquire();
4021 medium->getFirstRegistryMachineId(uuidRegistryParent);
4022 }
4023 rc = diff->init(mParent,
4024 medium->getPreferredDiffFormat(),
4025 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4026 uuidRegistryParent);
4027 if (FAILED(rc)) return rc;
4028
4029 /* Apply the normal locking logic to the entire chain. */
4030 MediumLockList *pMediumLockList(new MediumLockList());
4031 mediumLock.release();
4032 treeLock.release();
4033 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4034 true /* fMediumLockWrite */,
4035 medium,
4036 *pMediumLockList);
4037 treeLock.acquire();
4038 mediumLock.acquire();
4039 if (SUCCEEDED(rc))
4040 {
4041 mediumLock.release();
4042 treeLock.release();
4043 rc = pMediumLockList->Lock();
4044 treeLock.acquire();
4045 mediumLock.acquire();
4046 if (FAILED(rc))
4047 setError(rc,
4048 tr("Could not lock medium when creating diff '%s'"),
4049 diff->getLocationFull().c_str());
4050 else
4051 {
4052 /* will release the lock before the potentially lengthy
4053 * operation, so protect with the special state */
4054 MachineState_T oldState = mData->mMachineState;
4055 setMachineState(MachineState_SettingUp);
4056
4057 mediumLock.release();
4058 treeLock.release();
4059 alock.release();
4060
4061 rc = medium->createDiffStorage(diff,
4062 MediumVariant_Standard,
4063 pMediumLockList,
4064 NULL /* aProgress */,
4065 true /* aWait */);
4066
4067 alock.acquire();
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070
4071 setMachineState(oldState);
4072 }
4073 }
4074
4075 /* Unlock the media and free the associated memory. */
4076 delete pMediumLockList;
4077
4078 if (FAILED(rc)) return rc;
4079
4080 /* use the created diff for the actual attachment */
4081 medium = diff;
4082 mediumCaller.attach(medium);
4083 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4084 mediumLock.attach(medium);
4085 }
4086 while (0);
4087
4088 ComObjPtr<MediumAttachment> attachment;
4089 attachment.createObject();
4090 rc = attachment->init(this,
4091 medium,
4092 aControllerName,
4093 aControllerPort,
4094 aDevice,
4095 aType,
4096 fIndirect,
4097 false /* fPassthrough */,
4098 false /* fTempEject */,
4099 false /* fNonRotational */,
4100 false /* fDiscard */,
4101 Utf8Str::Empty);
4102 if (FAILED(rc)) return rc;
4103
4104 if (associate && !medium.isNull())
4105 {
4106 // as the last step, associate the medium to the VM
4107 rc = medium->addBackReference(mData->mUuid);
4108 // here we can fail because of Deleting, or being in process of creating a Diff
4109 if (FAILED(rc)) return rc;
4110
4111 mediumLock.release();
4112 treeLock.release();
4113 alock.release();
4114 addMediumToRegistry(medium);
4115 alock.acquire();
4116 treeLock.acquire();
4117 mediumLock.acquire();
4118 }
4119
4120 /* success: finally remember the attachment */
4121 setModified(IsModified_Storage);
4122 mMediaData.backup();
4123 mMediaData->mAttachments.push_back(attachment);
4124
4125 mediumLock.release();
4126 treeLock.release();
4127 alock.release();
4128
4129 if (fHotplug)
4130 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
4131
4132 mParent->saveModifiedRegistries();
4133
4134 return rc;
4135}
4136
4137STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4138 LONG aDevice)
4139{
4140 CheckComArgStrNotEmptyOrNull(aControllerName);
4141
4142 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4143 aControllerName, aControllerPort, aDevice));
4144
4145 AutoCaller autoCaller(this);
4146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4147
4148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4149
4150 HRESULT rc = checkStateDependency(MutableStateDep);
4151 if (FAILED(rc)) return rc;
4152
4153 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4154
4155 /* Check for an existing controller. */
4156 ComObjPtr<StorageController> ctl;
4157 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4158 if (FAILED(rc)) return rc;
4159
4160 StorageControllerType_T ctrlType;
4161 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4162 if (FAILED(rc))
4163 return setError(E_FAIL,
4164 tr("Could not get type of controller '%ls'"),
4165 aControllerName);
4166
4167 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4168 bool fHotplug = false;
4169 if (Global::IsOnlineOrTransient(mData->mMachineState))
4170 fHotplug = true;
4171
4172 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4173 return setError(VBOX_E_INVALID_VM_STATE,
4174 tr("Controller '%ls' does not support hotplugging"),
4175 aControllerName);
4176
4177 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4178 aControllerName,
4179 aControllerPort,
4180 aDevice);
4181 if (!pAttach)
4182 return setError(VBOX_E_OBJECT_NOT_FOUND,
4183 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4184 aDevice, aControllerPort, aControllerName);
4185
4186 /*
4187 * The VM has to detach the device before we delete any implicit diffs.
4188 * If this fails we can roll back without loosing data.
4189 */
4190 if (fHotplug)
4191 {
4192 alock.release();
4193 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
4194 alock.acquire();
4195 }
4196 if (FAILED(rc)) return rc;
4197
4198 /* If we are here everything went well and we can delete the implicit now. */
4199 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4200
4201 alock.release();
4202
4203 mParent->saveModifiedRegistries();
4204
4205 return rc;
4206}
4207
4208STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4209 LONG aDevice, BOOL aPassthrough)
4210{
4211 CheckComArgStrNotEmptyOrNull(aControllerName);
4212
4213 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4214 aControllerName, aControllerPort, aDevice, aPassthrough));
4215
4216 AutoCaller autoCaller(this);
4217 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4218
4219 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4220
4221 HRESULT rc = checkStateDependency(MutableStateDep);
4222 if (FAILED(rc)) return rc;
4223
4224 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4225
4226 if (Global::IsOnlineOrTransient(mData->mMachineState))
4227 return setError(VBOX_E_INVALID_VM_STATE,
4228 tr("Invalid machine state: %s"),
4229 Global::stringifyMachineState(mData->mMachineState));
4230
4231 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4232 aControllerName,
4233 aControllerPort,
4234 aDevice);
4235 if (!pAttach)
4236 return setError(VBOX_E_OBJECT_NOT_FOUND,
4237 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4238 aDevice, aControllerPort, aControllerName);
4239
4240
4241 setModified(IsModified_Storage);
4242 mMediaData.backup();
4243
4244 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4245
4246 if (pAttach->getType() != DeviceType_DVD)
4247 return setError(E_INVALIDARG,
4248 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4249 aDevice, aControllerPort, aControllerName);
4250 pAttach->updatePassthrough(!!aPassthrough);
4251
4252 return S_OK;
4253}
4254
4255STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4256 LONG aDevice, BOOL aTemporaryEject)
4257{
4258 CheckComArgStrNotEmptyOrNull(aControllerName);
4259
4260 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4261 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4262
4263 AutoCaller autoCaller(this);
4264 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4265
4266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4267
4268 HRESULT rc = checkStateDependency(MutableStateDep);
4269 if (FAILED(rc)) return rc;
4270
4271 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4272 aControllerName,
4273 aControllerPort,
4274 aDevice);
4275 if (!pAttach)
4276 return setError(VBOX_E_OBJECT_NOT_FOUND,
4277 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4278 aDevice, aControllerPort, aControllerName);
4279
4280
4281 setModified(IsModified_Storage);
4282 mMediaData.backup();
4283
4284 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4285
4286 if (pAttach->getType() != DeviceType_DVD)
4287 return setError(E_INVALIDARG,
4288 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4289 aDevice, aControllerPort, aControllerName);
4290 pAttach->updateTempEject(!!aTemporaryEject);
4291
4292 return S_OK;
4293}
4294
4295STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4296 LONG aDevice, BOOL aNonRotational)
4297{
4298 CheckComArgStrNotEmptyOrNull(aControllerName);
4299
4300 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4301 aControllerName, aControllerPort, aDevice, aNonRotational));
4302
4303 AutoCaller autoCaller(this);
4304 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4305
4306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4307
4308 HRESULT rc = checkStateDependency(MutableStateDep);
4309 if (FAILED(rc)) return rc;
4310
4311 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4312
4313 if (Global::IsOnlineOrTransient(mData->mMachineState))
4314 return setError(VBOX_E_INVALID_VM_STATE,
4315 tr("Invalid machine state: %s"),
4316 Global::stringifyMachineState(mData->mMachineState));
4317
4318 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4319 aControllerName,
4320 aControllerPort,
4321 aDevice);
4322 if (!pAttach)
4323 return setError(VBOX_E_OBJECT_NOT_FOUND,
4324 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4325 aDevice, aControllerPort, aControllerName);
4326
4327
4328 setModified(IsModified_Storage);
4329 mMediaData.backup();
4330
4331 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4332
4333 if (pAttach->getType() != DeviceType_HardDisk)
4334 return setError(E_INVALIDARG,
4335 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4336 aDevice, aControllerPort, aControllerName);
4337 pAttach->updateNonRotational(!!aNonRotational);
4338
4339 return S_OK;
4340}
4341
4342STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4343 LONG aDevice, BOOL aDiscard)
4344{
4345 CheckComArgStrNotEmptyOrNull(aControllerName);
4346
4347 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4348 aControllerName, aControllerPort, aDevice, aDiscard));
4349
4350 AutoCaller autoCaller(this);
4351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4352
4353 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4354
4355 HRESULT rc = checkStateDependency(MutableStateDep);
4356 if (FAILED(rc)) return rc;
4357
4358 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4359
4360 if (Global::IsOnlineOrTransient(mData->mMachineState))
4361 return setError(VBOX_E_INVALID_VM_STATE,
4362 tr("Invalid machine state: %s"),
4363 Global::stringifyMachineState(mData->mMachineState));
4364
4365 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4366 aControllerName,
4367 aControllerPort,
4368 aDevice);
4369 if (!pAttach)
4370 return setError(VBOX_E_OBJECT_NOT_FOUND,
4371 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4372 aDevice, aControllerPort, aControllerName);
4373
4374
4375 setModified(IsModified_Storage);
4376 mMediaData.backup();
4377
4378 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4379
4380 if (pAttach->getType() != DeviceType_HardDisk)
4381 return setError(E_INVALIDARG,
4382 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
4383 aDevice, aControllerPort, aControllerName);
4384 pAttach->updateDiscard(!!aDiscard);
4385
4386 return S_OK;
4387}
4388
4389STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4390 LONG aDevice)
4391{
4392 int rc = S_OK;
4393 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4394 aControllerName, aControllerPort, aDevice));
4395
4396 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4397
4398 return rc;
4399}
4400
4401STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4402 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4403{
4404 CheckComArgStrNotEmptyOrNull(aControllerName);
4405
4406 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4407 aControllerName, aControllerPort, aDevice));
4408
4409 AutoCaller autoCaller(this);
4410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4411
4412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4413
4414 HRESULT rc = checkStateDependency(MutableStateDep);
4415 if (FAILED(rc)) return rc;
4416
4417 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4418
4419 if (Global::IsOnlineOrTransient(mData->mMachineState))
4420 return setError(VBOX_E_INVALID_VM_STATE,
4421 tr("Invalid machine state: %s"),
4422 Global::stringifyMachineState(mData->mMachineState));
4423
4424 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4425 aControllerName,
4426 aControllerPort,
4427 aDevice);
4428 if (!pAttach)
4429 return setError(VBOX_E_OBJECT_NOT_FOUND,
4430 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4431 aDevice, aControllerPort, aControllerName);
4432
4433
4434 setModified(IsModified_Storage);
4435 mMediaData.backup();
4436
4437 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4438 if (aBandwidthGroup && group.isNull())
4439 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4440
4441 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4442
4443 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4444 if (strBandwidthGroupOld.isNotEmpty())
4445 {
4446 /* Get the bandwidth group object and release it - this must not fail. */
4447 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4448 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4449 Assert(SUCCEEDED(rc));
4450
4451 pBandwidthGroupOld->release();
4452 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4453 }
4454
4455 if (!group.isNull())
4456 {
4457 group->reference();
4458 pAttach->updateBandwidthGroup(group->getName());
4459 }
4460
4461 return S_OK;
4462}
4463
4464STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4465 LONG aControllerPort,
4466 LONG aDevice,
4467 DeviceType_T aType)
4468{
4469 HRESULT rc = S_OK;
4470
4471 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4472 aControllerName, aControllerPort, aDevice, aType));
4473
4474 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4475
4476 return rc;
4477}
4478
4479
4480
4481STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4482 LONG aControllerPort,
4483 LONG aDevice,
4484 BOOL aForce)
4485{
4486 int rc = S_OK;
4487 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4488 aControllerName, aControllerPort, aForce));
4489
4490 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4491
4492 return rc;
4493}
4494
4495STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4496 LONG aControllerPort,
4497 LONG aDevice,
4498 IMedium *aMedium,
4499 BOOL aForce)
4500{
4501 int rc = S_OK;
4502 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4503 aControllerName, aControllerPort, aDevice, aForce));
4504
4505 CheckComArgStrNotEmptyOrNull(aControllerName);
4506
4507 AutoCaller autoCaller(this);
4508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4509
4510 // request the host lock first, since might be calling Host methods for getting host drives;
4511 // next, protect the media tree all the while we're in here, as well as our member variables
4512 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4513 this->lockHandle(),
4514 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4515
4516 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4517 aControllerName,
4518 aControllerPort,
4519 aDevice);
4520 if (pAttach.isNull())
4521 return setError(VBOX_E_OBJECT_NOT_FOUND,
4522 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4523 aDevice, aControllerPort, aControllerName);
4524
4525 /* Remember previously mounted medium. The medium before taking the
4526 * backup is not necessarily the same thing. */
4527 ComObjPtr<Medium> oldmedium;
4528 oldmedium = pAttach->getMedium();
4529
4530 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4531 if (aMedium && pMedium.isNull())
4532 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4533
4534 AutoCaller mediumCaller(pMedium);
4535 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4536
4537 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4538 if (pMedium)
4539 {
4540 DeviceType_T mediumType = pAttach->getType();
4541 switch (mediumType)
4542 {
4543 case DeviceType_DVD:
4544 case DeviceType_Floppy:
4545 break;
4546
4547 default:
4548 return setError(VBOX_E_INVALID_OBJECT_STATE,
4549 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4550 aControllerPort,
4551 aDevice,
4552 aControllerName);
4553 }
4554 }
4555
4556 setModified(IsModified_Storage);
4557 mMediaData.backup();
4558
4559 {
4560 // The backup operation makes the pAttach reference point to the
4561 // old settings. Re-get the correct reference.
4562 pAttach = findAttachment(mMediaData->mAttachments,
4563 aControllerName,
4564 aControllerPort,
4565 aDevice);
4566 if (!oldmedium.isNull())
4567 oldmedium->removeBackReference(mData->mUuid);
4568 if (!pMedium.isNull())
4569 {
4570 pMedium->addBackReference(mData->mUuid);
4571
4572 mediumLock.release();
4573 multiLock.release();
4574 addMediumToRegistry(pMedium);
4575 multiLock.acquire();
4576 mediumLock.acquire();
4577 }
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580 pAttach->updateMedium(pMedium);
4581 }
4582
4583 setModified(IsModified_Storage);
4584
4585 mediumLock.release();
4586 multiLock.release();
4587 rc = onMediumChange(pAttach, aForce);
4588 multiLock.acquire();
4589 mediumLock.acquire();
4590
4591 /* On error roll back this change only. */
4592 if (FAILED(rc))
4593 {
4594 if (!pMedium.isNull())
4595 pMedium->removeBackReference(mData->mUuid);
4596 pAttach = findAttachment(mMediaData->mAttachments,
4597 aControllerName,
4598 aControllerPort,
4599 aDevice);
4600 /* If the attachment is gone in the meantime, bail out. */
4601 if (pAttach.isNull())
4602 return rc;
4603 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4604 if (!oldmedium.isNull())
4605 oldmedium->addBackReference(mData->mUuid);
4606 pAttach->updateMedium(oldmedium);
4607 }
4608
4609 mediumLock.release();
4610 multiLock.release();
4611
4612 mParent->saveModifiedRegistries();
4613
4614 return rc;
4615}
4616
4617STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4618 LONG aControllerPort,
4619 LONG aDevice,
4620 IMedium **aMedium)
4621{
4622 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4623 aControllerName, aControllerPort, aDevice));
4624
4625 CheckComArgStrNotEmptyOrNull(aControllerName);
4626 CheckComArgOutPointerValid(aMedium);
4627
4628 AutoCaller autoCaller(this);
4629 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4630
4631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 *aMedium = NULL;
4634
4635 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4636 aControllerName,
4637 aControllerPort,
4638 aDevice);
4639 if (pAttach.isNull())
4640 return setError(VBOX_E_OBJECT_NOT_FOUND,
4641 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4642 aDevice, aControllerPort, aControllerName);
4643
4644 pAttach->getMedium().queryInterfaceTo(aMedium);
4645
4646 return S_OK;
4647}
4648
4649STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4650{
4651 CheckComArgOutPointerValid(port);
4652 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4653
4654 AutoCaller autoCaller(this);
4655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4656
4657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4658
4659 mSerialPorts[slot].queryInterfaceTo(port);
4660
4661 return S_OK;
4662}
4663
4664STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4665{
4666 CheckComArgOutPointerValid(port);
4667 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4668
4669 AutoCaller autoCaller(this);
4670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4671
4672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4673
4674 mParallelPorts[slot].queryInterfaceTo(port);
4675
4676 return S_OK;
4677}
4678
4679STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4680{
4681 CheckComArgOutPointerValid(adapter);
4682 /* Do not assert if slot is out of range, just return the advertised
4683 status. testdriver/vbox.py triggers this in logVmInfo. */
4684 if (slot >= mNetworkAdapters.size())
4685 return setError(E_INVALIDARG,
4686 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4687 slot, mNetworkAdapters.size());
4688
4689 AutoCaller autoCaller(this);
4690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4691
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4695
4696 return S_OK;
4697}
4698
4699STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4700{
4701 CheckComArgOutSafeArrayPointerValid(aKeys);
4702
4703 AutoCaller autoCaller(this);
4704 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4705
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4709 int i = 0;
4710 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4711 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4712 ++it, ++i)
4713 {
4714 const Utf8Str &strKey = it->first;
4715 strKey.cloneTo(&saKeys[i]);
4716 }
4717 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4718
4719 return S_OK;
4720 }
4721
4722 /**
4723 * @note Locks this object for reading.
4724 */
4725STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4726 BSTR *aValue)
4727{
4728 CheckComArgStrNotEmptyOrNull(aKey);
4729 CheckComArgOutPointerValid(aValue);
4730
4731 AutoCaller autoCaller(this);
4732 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4733
4734 /* start with nothing found */
4735 Bstr bstrResult("");
4736
4737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4738
4739 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4740 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4741 // found:
4742 bstrResult = it->second; // source is a Utf8Str
4743
4744 /* return the result to caller (may be empty) */
4745 bstrResult.cloneTo(aValue);
4746
4747 return S_OK;
4748}
4749
4750 /**
4751 * @note Locks mParent for writing + this object for writing.
4752 */
4753STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4754{
4755 CheckComArgStrNotEmptyOrNull(aKey);
4756
4757 AutoCaller autoCaller(this);
4758 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4759
4760 Utf8Str strKey(aKey);
4761 Utf8Str strValue(aValue);
4762 Utf8Str strOldValue; // empty
4763
4764 // locking note: we only hold the read lock briefly to look up the old value,
4765 // then release it and call the onExtraCanChange callbacks. There is a small
4766 // chance of a race insofar as the callback might be called twice if two callers
4767 // change the same key at the same time, but that's a much better solution
4768 // than the deadlock we had here before. The actual changing of the extradata
4769 // is then performed under the write lock and race-free.
4770
4771 // look up the old value first; if nothing has changed then we need not do anything
4772 {
4773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4774 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4775 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4776 strOldValue = it->second;
4777 }
4778
4779 bool fChanged;
4780 if ((fChanged = (strOldValue != strValue)))
4781 {
4782 // ask for permission from all listeners outside the locks;
4783 // onExtraDataCanChange() only briefly requests the VirtualBox
4784 // lock to copy the list of callbacks to invoke
4785 Bstr error;
4786 Bstr bstrValue(aValue);
4787
4788 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4789 {
4790 const char *sep = error.isEmpty() ? "" : ": ";
4791 CBSTR err = error.raw();
4792 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4793 sep, err));
4794 return setError(E_ACCESSDENIED,
4795 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4796 aKey,
4797 bstrValue.raw(),
4798 sep,
4799 err);
4800 }
4801
4802 // data is changing and change not vetoed: then write it out under the lock
4803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4804
4805 if (isSnapshotMachine())
4806 {
4807 HRESULT rc = checkStateDependency(MutableStateDep);
4808 if (FAILED(rc)) return rc;
4809 }
4810
4811 if (strValue.isEmpty())
4812 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4813 else
4814 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4815 // creates a new key if needed
4816
4817 bool fNeedsGlobalSaveSettings = false;
4818 saveSettings(&fNeedsGlobalSaveSettings);
4819
4820 if (fNeedsGlobalSaveSettings)
4821 {
4822 // save the global settings; for that we should hold only the VirtualBox lock
4823 alock.release();
4824 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4825 mParent->saveSettings();
4826 }
4827 }
4828
4829 // fire notification outside the lock
4830 if (fChanged)
4831 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4832
4833 return S_OK;
4834}
4835
4836STDMETHODIMP Machine::SaveSettings()
4837{
4838 AutoCaller autoCaller(this);
4839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4840
4841 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4842
4843 /* when there was auto-conversion, we want to save the file even if
4844 * the VM is saved */
4845 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4846 if (FAILED(rc)) return rc;
4847
4848 /* the settings file path may never be null */
4849 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4850
4851 /* save all VM data excluding snapshots */
4852 bool fNeedsGlobalSaveSettings = false;
4853 rc = saveSettings(&fNeedsGlobalSaveSettings);
4854 mlock.release();
4855
4856 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4857 {
4858 // save the global settings; for that we should hold only the VirtualBox lock
4859 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4860 rc = mParent->saveSettings();
4861 }
4862
4863 return rc;
4864}
4865
4866STDMETHODIMP Machine::DiscardSettings()
4867{
4868 AutoCaller autoCaller(this);
4869 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4870
4871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4872
4873 HRESULT rc = checkStateDependency(MutableStateDep);
4874 if (FAILED(rc)) return rc;
4875
4876 /*
4877 * during this rollback, the session will be notified if data has
4878 * been actually changed
4879 */
4880 rollback(true /* aNotify */);
4881
4882 return S_OK;
4883}
4884
4885/** @note Locks objects! */
4886STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
4887 ComSafeArrayOut(IMedium*, aMedia))
4888{
4889 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
4890 AutoLimitedCaller autoCaller(this);
4891 AssertComRCReturnRC(autoCaller.rc());
4892
4893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 Guid id(getId());
4896
4897 if (mData->mSession.mState != SessionState_Unlocked)
4898 return setError(VBOX_E_INVALID_OBJECT_STATE,
4899 tr("Cannot unregister the machine '%s' while it is locked"),
4900 mUserData->s.strName.c_str());
4901
4902 // wait for state dependents to drop to zero
4903 ensureNoStateDependencies();
4904
4905 if (!mData->mAccessible)
4906 {
4907 // inaccessible maschines can only be unregistered; uninitialize ourselves
4908 // here because currently there may be no unregistered that are inaccessible
4909 // (this state combination is not supported). Note releasing the caller and
4910 // leaving the lock before calling uninit()
4911 alock.release();
4912 autoCaller.release();
4913
4914 uninit();
4915
4916 mParent->unregisterMachine(this, id);
4917 // calls VirtualBox::saveSettings()
4918
4919 return S_OK;
4920 }
4921
4922 HRESULT rc = S_OK;
4923
4924 // discard saved state
4925 if (mData->mMachineState == MachineState_Saved)
4926 {
4927 // add the saved state file to the list of files the caller should delete
4928 Assert(!mSSData->strStateFilePath.isEmpty());
4929 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4930
4931 mSSData->strStateFilePath.setNull();
4932
4933 // unconditionally set the machine state to powered off, we now
4934 // know no session has locked the machine
4935 mData->mMachineState = MachineState_PoweredOff;
4936 }
4937
4938 size_t cSnapshots = 0;
4939 if (mData->mFirstSnapshot)
4940 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
4941 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
4942 // fail now before we start detaching media
4943 return setError(VBOX_E_INVALID_OBJECT_STATE,
4944 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
4945 mUserData->s.strName.c_str(), cSnapshots);
4946
4947 // This list collects the medium objects from all medium attachments
4948 // which we will detach from the machine and its snapshots, in a specific
4949 // order which allows for closing all media without getting "media in use"
4950 // errors, simply by going through the list from the front to the back:
4951 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4952 // and must be closed before the parent media from the snapshots, or closing the parents
4953 // will fail because they still have children);
4954 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4955 // the root ("first") snapshot of the machine.
4956 MediaList llMedia;
4957
4958 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
4959 && mMediaData->mAttachments.size()
4960 )
4961 {
4962 // we have media attachments: detach them all and add the Medium objects to our list
4963 if (cleanupMode != CleanupMode_UnregisterOnly)
4964 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
4965 else
4966 return setError(VBOX_E_INVALID_OBJECT_STATE,
4967 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
4968 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
4969 }
4970
4971 if (cSnapshots)
4972 {
4973 // autoCleanup must be true here, or we would have failed above
4974
4975 // add the media from the medium attachments of the snapshots to llMedia
4976 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4977 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4978 // into the children first
4979
4980 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4981 MachineState_T oldState = mData->mMachineState;
4982 mData->mMachineState = MachineState_DeletingSnapshot;
4983
4984 // make a copy of the first snapshot so the refcount does not drop to 0
4985 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
4986 // because of the AutoCaller voodoo)
4987 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4988
4989 // GO!
4990 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
4991
4992 mData->mMachineState = oldState;
4993 }
4994
4995 if (FAILED(rc))
4996 {
4997 rollbackMedia();
4998 return rc;
4999 }
5000
5001 // commit all the media changes made above
5002 commitMedia();
5003
5004 mData->mRegistered = false;
5005
5006 // machine lock no longer needed
5007 alock.release();
5008
5009 // return media to caller
5010 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5011 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5012
5013 mParent->unregisterMachine(this, id);
5014 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5015
5016 return S_OK;
5017}
5018
5019struct Machine::DeleteTask
5020{
5021 ComObjPtr<Machine> pMachine;
5022 RTCList<ComPtr<IMedium> > llMediums;
5023 StringsList llFilesToDelete;
5024 ComObjPtr<Progress> pProgress;
5025};
5026
5027STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5028{
5029 LogFlowFuncEnter();
5030
5031 AutoCaller autoCaller(this);
5032 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5033
5034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5035
5036 HRESULT rc = checkStateDependency(MutableStateDep);
5037 if (FAILED(rc)) return rc;
5038
5039 if (mData->mRegistered)
5040 return setError(VBOX_E_INVALID_VM_STATE,
5041 tr("Cannot delete settings of a registered machine"));
5042
5043 DeleteTask *pTask = new DeleteTask;
5044 pTask->pMachine = this;
5045 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5046
5047 // collect files to delete
5048 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5049
5050 for (size_t i = 0; i < sfaMedia.size(); ++i)
5051 {
5052 IMedium *pIMedium(sfaMedia[i]);
5053 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5054 if (pMedium.isNull())
5055 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5056 SafeArray<BSTR> ids;
5057 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5058 if (FAILED(rc)) return rc;
5059 /* At this point the medium should not have any back references
5060 * anymore. If it has it is attached to another VM and *must* not
5061 * deleted. */
5062 if (ids.size() < 1)
5063 pTask->llMediums.append(pMedium);
5064 }
5065 if (mData->pMachineConfigFile->fileExists())
5066 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5067
5068 pTask->pProgress.createObject();
5069 pTask->pProgress->init(getVirtualBox(),
5070 static_cast<IMachine*>(this) /* aInitiator */,
5071 Bstr(tr("Deleting files")).raw(),
5072 true /* fCancellable */,
5073 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5074 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5075
5076 int vrc = RTThreadCreate(NULL,
5077 Machine::deleteThread,
5078 (void*)pTask,
5079 0,
5080 RTTHREADTYPE_MAIN_WORKER,
5081 0,
5082 "MachineDelete");
5083
5084 pTask->pProgress.queryInterfaceTo(aProgress);
5085
5086 if (RT_FAILURE(vrc))
5087 {
5088 delete pTask;
5089 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5090 }
5091
5092 LogFlowFuncLeave();
5093
5094 return S_OK;
5095}
5096
5097/**
5098 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5099 * calls Machine::deleteTaskWorker() on the actual machine object.
5100 * @param Thread
5101 * @param pvUser
5102 * @return
5103 */
5104/*static*/
5105DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5106{
5107 LogFlowFuncEnter();
5108
5109 DeleteTask *pTask = (DeleteTask*)pvUser;
5110 Assert(pTask);
5111 Assert(pTask->pMachine);
5112 Assert(pTask->pProgress);
5113
5114 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5115 pTask->pProgress->notifyComplete(rc);
5116
5117 delete pTask;
5118
5119 LogFlowFuncLeave();
5120
5121 NOREF(Thread);
5122
5123 return VINF_SUCCESS;
5124}
5125
5126/**
5127 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5128 * @param task
5129 * @return
5130 */
5131HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5132{
5133 AutoCaller autoCaller(this);
5134 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5135
5136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5137
5138 HRESULT rc = S_OK;
5139
5140 try
5141 {
5142 ULONG uLogHistoryCount = 3;
5143 ComPtr<ISystemProperties> systemProperties;
5144 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5145 if (FAILED(rc)) throw rc;
5146
5147 if (!systemProperties.isNull())
5148 {
5149 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5150 if (FAILED(rc)) throw rc;
5151 }
5152
5153 MachineState_T oldState = mData->mMachineState;
5154 setMachineState(MachineState_SettingUp);
5155 alock.release();
5156 for (size_t i = 0; i < task.llMediums.size(); ++i)
5157 {
5158 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5159 {
5160 AutoCaller mac(pMedium);
5161 if (FAILED(mac.rc())) throw mac.rc();
5162 Utf8Str strLocation = pMedium->getLocationFull();
5163 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5164 if (FAILED(rc)) throw rc;
5165 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5166 }
5167 ComPtr<IProgress> pProgress2;
5168 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5169 if (FAILED(rc)) throw rc;
5170 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5171 if (FAILED(rc)) throw rc;
5172 /* Check the result of the asynchrony process. */
5173 LONG iRc;
5174 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5175 if (FAILED(rc)) throw rc;
5176 /* If the thread of the progress object has an error, then
5177 * retrieve the error info from there, or it'll be lost. */
5178 if (FAILED(iRc))
5179 throw setError(ProgressErrorInfo(pProgress2));
5180 }
5181 setMachineState(oldState);
5182 alock.acquire();
5183
5184 // delete the files pushed on the task list by Machine::Delete()
5185 // (this includes saved states of the machine and snapshots and
5186 // medium storage files from the IMedium list passed in, and the
5187 // machine XML file)
5188 StringsList::const_iterator it = task.llFilesToDelete.begin();
5189 while (it != task.llFilesToDelete.end())
5190 {
5191 const Utf8Str &strFile = *it;
5192 LogFunc(("Deleting file %s\n", strFile.c_str()));
5193 int vrc = RTFileDelete(strFile.c_str());
5194 if (RT_FAILURE(vrc))
5195 throw setError(VBOX_E_IPRT_ERROR,
5196 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5197
5198 ++it;
5199 if (it == task.llFilesToDelete.end())
5200 {
5201 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5202 if (FAILED(rc)) throw rc;
5203 break;
5204 }
5205
5206 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5207 if (FAILED(rc)) throw rc;
5208 }
5209
5210 /* delete the settings only when the file actually exists */
5211 if (mData->pMachineConfigFile->fileExists())
5212 {
5213 /* Delete any backup or uncommitted XML files. Ignore failures.
5214 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5215 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5216 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5217 RTFileDelete(otherXml.c_str());
5218 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5219 RTFileDelete(otherXml.c_str());
5220
5221 /* delete the Logs folder, nothing important should be left
5222 * there (we don't check for errors because the user might have
5223 * some private files there that we don't want to delete) */
5224 Utf8Str logFolder;
5225 getLogFolder(logFolder);
5226 Assert(logFolder.length());
5227 if (RTDirExists(logFolder.c_str()))
5228 {
5229 /* Delete all VBox.log[.N] files from the Logs folder
5230 * (this must be in sync with the rotation logic in
5231 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5232 * files that may have been created by the GUI. */
5233 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5234 logFolder.c_str(), RTPATH_DELIMITER);
5235 RTFileDelete(log.c_str());
5236 log = Utf8StrFmt("%s%cVBox.png",
5237 logFolder.c_str(), RTPATH_DELIMITER);
5238 RTFileDelete(log.c_str());
5239 for (int i = uLogHistoryCount; i > 0; i--)
5240 {
5241 log = Utf8StrFmt("%s%cVBox.log.%d",
5242 logFolder.c_str(), RTPATH_DELIMITER, i);
5243 RTFileDelete(log.c_str());
5244 log = Utf8StrFmt("%s%cVBox.png.%d",
5245 logFolder.c_str(), RTPATH_DELIMITER, i);
5246 RTFileDelete(log.c_str());
5247 }
5248
5249 RTDirRemove(logFolder.c_str());
5250 }
5251
5252 /* delete the Snapshots folder, nothing important should be left
5253 * there (we don't check for errors because the user might have
5254 * some private files there that we don't want to delete) */
5255 Utf8Str strFullSnapshotFolder;
5256 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5257 Assert(!strFullSnapshotFolder.isEmpty());
5258 if (RTDirExists(strFullSnapshotFolder.c_str()))
5259 RTDirRemove(strFullSnapshotFolder.c_str());
5260
5261 // delete the directory that contains the settings file, but only
5262 // if it matches the VM name
5263 Utf8Str settingsDir;
5264 if (isInOwnDir(&settingsDir))
5265 RTDirRemove(settingsDir.c_str());
5266 }
5267
5268 alock.release();
5269
5270 mParent->saveModifiedRegistries();
5271 }
5272 catch (HRESULT aRC) { rc = aRC; }
5273
5274 return rc;
5275}
5276
5277STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5278{
5279 CheckComArgOutPointerValid(aSnapshot);
5280
5281 AutoCaller autoCaller(this);
5282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5283
5284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5285
5286 ComObjPtr<Snapshot> pSnapshot;
5287 HRESULT rc;
5288
5289 if (!aNameOrId || !*aNameOrId)
5290 // null case (caller wants root snapshot): findSnapshotById() handles this
5291 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5292 else
5293 {
5294 Guid uuid(aNameOrId);
5295 if (!uuid.isEmpty())
5296 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5297 else
5298 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5299 }
5300 pSnapshot.queryInterfaceTo(aSnapshot);
5301
5302 return rc;
5303}
5304
5305STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5306{
5307 CheckComArgStrNotEmptyOrNull(aName);
5308 CheckComArgStrNotEmptyOrNull(aHostPath);
5309
5310 AutoCaller autoCaller(this);
5311 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5312
5313 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5314
5315 HRESULT rc = checkStateDependency(MutableStateDep);
5316 if (FAILED(rc)) return rc;
5317
5318 Utf8Str strName(aName);
5319
5320 ComObjPtr<SharedFolder> sharedFolder;
5321 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5322 if (SUCCEEDED(rc))
5323 return setError(VBOX_E_OBJECT_IN_USE,
5324 tr("Shared folder named '%s' already exists"),
5325 strName.c_str());
5326
5327 sharedFolder.createObject();
5328 rc = sharedFolder->init(getMachine(),
5329 strName,
5330 aHostPath,
5331 !!aWritable,
5332 !!aAutoMount,
5333 true /* fFailOnError */);
5334 if (FAILED(rc)) return rc;
5335
5336 setModified(IsModified_SharedFolders);
5337 mHWData.backup();
5338 mHWData->mSharedFolders.push_back(sharedFolder);
5339
5340 /* inform the direct session if any */
5341 alock.release();
5342 onSharedFolderChange();
5343
5344 return S_OK;
5345}
5346
5347STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5348{
5349 CheckComArgStrNotEmptyOrNull(aName);
5350
5351 AutoCaller autoCaller(this);
5352 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5353
5354 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5355
5356 HRESULT rc = checkStateDependency(MutableStateDep);
5357 if (FAILED(rc)) return rc;
5358
5359 ComObjPtr<SharedFolder> sharedFolder;
5360 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5361 if (FAILED(rc)) return rc;
5362
5363 setModified(IsModified_SharedFolders);
5364 mHWData.backup();
5365 mHWData->mSharedFolders.remove(sharedFolder);
5366
5367 /* inform the direct session if any */
5368 alock.release();
5369 onSharedFolderChange();
5370
5371 return S_OK;
5372}
5373
5374STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5375{
5376 CheckComArgOutPointerValid(aCanShow);
5377
5378 /* start with No */
5379 *aCanShow = FALSE;
5380
5381 AutoCaller autoCaller(this);
5382 AssertComRCReturnRC(autoCaller.rc());
5383
5384 ComPtr<IInternalSessionControl> directControl;
5385 {
5386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 if (mData->mSession.mState != SessionState_Locked)
5389 return setError(VBOX_E_INVALID_VM_STATE,
5390 tr("Machine is not locked for session (session state: %s)"),
5391 Global::stringifySessionState(mData->mSession.mState));
5392
5393 directControl = mData->mSession.mDirectControl;
5394 }
5395
5396 /* ignore calls made after #OnSessionEnd() is called */
5397 if (!directControl)
5398 return S_OK;
5399
5400 LONG64 dummy;
5401 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5402}
5403
5404STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5405{
5406 CheckComArgOutPointerValid(aWinId);
5407
5408 AutoCaller autoCaller(this);
5409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5410
5411 ComPtr<IInternalSessionControl> directControl;
5412 {
5413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5414
5415 if (mData->mSession.mState != SessionState_Locked)
5416 return setError(E_FAIL,
5417 tr("Machine is not locked for session (session state: %s)"),
5418 Global::stringifySessionState(mData->mSession.mState));
5419
5420 directControl = mData->mSession.mDirectControl;
5421 }
5422
5423 /* ignore calls made after #OnSessionEnd() is called */
5424 if (!directControl)
5425 return S_OK;
5426
5427 BOOL dummy;
5428 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5429}
5430
5431#ifdef VBOX_WITH_GUEST_PROPS
5432/**
5433 * Look up a guest property in VBoxSVC's internal structures.
5434 */
5435HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5436 BSTR *aValue,
5437 LONG64 *aTimestamp,
5438 BSTR *aFlags) const
5439{
5440 using namespace guestProp;
5441
5442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5443 Utf8Str strName(aName);
5444 HWData::GuestPropertyList::const_iterator it;
5445
5446 for (it = mHWData->mGuestProperties.begin();
5447 it != mHWData->mGuestProperties.end(); ++it)
5448 {
5449 if (it->strName == strName)
5450 {
5451 char szFlags[MAX_FLAGS_LEN + 1];
5452 it->strValue.cloneTo(aValue);
5453 *aTimestamp = it->mTimestamp;
5454 writeFlags(it->mFlags, szFlags);
5455 Bstr(szFlags).cloneTo(aFlags);
5456 break;
5457 }
5458 }
5459 return S_OK;
5460}
5461
5462/**
5463 * Query the VM that a guest property belongs to for the property.
5464 * @returns E_ACCESSDENIED if the VM process is not available or not
5465 * currently handling queries and the lookup should then be done in
5466 * VBoxSVC.
5467 */
5468HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5469 BSTR *aValue,
5470 LONG64 *aTimestamp,
5471 BSTR *aFlags) const
5472{
5473 HRESULT rc;
5474 ComPtr<IInternalSessionControl> directControl;
5475 directControl = mData->mSession.mDirectControl;
5476
5477 /* fail if we were called after #OnSessionEnd() is called. This is a
5478 * silly race condition. */
5479
5480 if (!directControl)
5481 rc = E_ACCESSDENIED;
5482 else
5483 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5484 false /* isSetter */,
5485 aValue, aTimestamp, aFlags);
5486 return rc;
5487}
5488#endif // VBOX_WITH_GUEST_PROPS
5489
5490STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5491 BSTR *aValue,
5492 LONG64 *aTimestamp,
5493 BSTR *aFlags)
5494{
5495#ifndef VBOX_WITH_GUEST_PROPS
5496 ReturnComNotImplemented();
5497#else // VBOX_WITH_GUEST_PROPS
5498 CheckComArgStrNotEmptyOrNull(aName);
5499 CheckComArgOutPointerValid(aValue);
5500 CheckComArgOutPointerValid(aTimestamp);
5501 CheckComArgOutPointerValid(aFlags);
5502
5503 AutoCaller autoCaller(this);
5504 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5505
5506 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5507 if (rc == E_ACCESSDENIED)
5508 /* The VM is not running or the service is not (yet) accessible */
5509 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5510 return rc;
5511#endif // VBOX_WITH_GUEST_PROPS
5512}
5513
5514STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5515{
5516 LONG64 dummyTimestamp;
5517 Bstr dummyFlags;
5518 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5519}
5520
5521STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5522{
5523 Bstr dummyValue;
5524 Bstr dummyFlags;
5525 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5526}
5527
5528#ifdef VBOX_WITH_GUEST_PROPS
5529/**
5530 * Set a guest property in VBoxSVC's internal structures.
5531 */
5532HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5533 IN_BSTR aFlags)
5534{
5535 using namespace guestProp;
5536
5537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5538 HRESULT rc = S_OK;
5539 HWData::GuestProperty property;
5540 property.mFlags = NILFLAG;
5541 bool found = false;
5542
5543 rc = checkStateDependency(MutableStateDep);
5544 if (FAILED(rc)) return rc;
5545
5546 try
5547 {
5548 Utf8Str utf8Name(aName);
5549 Utf8Str utf8Flags(aFlags);
5550 uint32_t fFlags = NILFLAG;
5551 if ( (aFlags != NULL)
5552 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5553 )
5554 return setError(E_INVALIDARG,
5555 tr("Invalid flag values: '%ls'"),
5556 aFlags);
5557
5558 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I
5559 * know, this is simple and do an OK job atm.) */
5560 HWData::GuestPropertyList::iterator it;
5561 for (it = mHWData->mGuestProperties.begin();
5562 it != mHWData->mGuestProperties.end(); ++it)
5563 if (it->strName == utf8Name)
5564 {
5565 property = *it;
5566 if (it->mFlags & (RDONLYHOST))
5567 rc = setError(E_ACCESSDENIED,
5568 tr("The property '%ls' cannot be changed by the host"),
5569 aName);
5570 else
5571 {
5572 setModified(IsModified_MachineData);
5573 mHWData.backup(); // @todo r=dj backup in a loop?!?
5574
5575 /* The backup() operation invalidates our iterator, so
5576 * get a new one. */
5577 for (it = mHWData->mGuestProperties.begin();
5578 it->strName != utf8Name;
5579 ++it)
5580 ;
5581 mHWData->mGuestProperties.erase(it);
5582 }
5583 found = true;
5584 break;
5585 }
5586 if (found && SUCCEEDED(rc))
5587 {
5588 if (aValue)
5589 {
5590 RTTIMESPEC time;
5591 property.strValue = aValue;
5592 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5593 if (aFlags != NULL)
5594 property.mFlags = fFlags;
5595 mHWData->mGuestProperties.push_back(property);
5596 }
5597 }
5598 else if (SUCCEEDED(rc) && aValue)
5599 {
5600 RTTIMESPEC time;
5601 setModified(IsModified_MachineData);
5602 mHWData.backup();
5603 property.strName = aName;
5604 property.strValue = aValue;
5605 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5606 property.mFlags = fFlags;
5607 mHWData->mGuestProperties.push_back(property);
5608 }
5609 if ( SUCCEEDED(rc)
5610 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5611 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5612 RTSTR_MAX,
5613 utf8Name.c_str(),
5614 RTSTR_MAX,
5615 NULL)
5616 )
5617 )
5618 {
5619 /** @todo r=bird: Why aren't we leaving the lock here? The
5620 * same code in PushGuestProperty does... */
5621 mParent->onGuestPropertyChange(mData->mUuid, aName,
5622 aValue ? aValue : Bstr("").raw(),
5623 aFlags ? aFlags : Bstr("").raw());
5624 }
5625 }
5626 catch (std::bad_alloc &)
5627 {
5628 rc = E_OUTOFMEMORY;
5629 }
5630
5631 return rc;
5632}
5633
5634/**
5635 * Set a property on the VM that that property belongs to.
5636 * @returns E_ACCESSDENIED if the VM process is not available or not
5637 * currently handling queries and the setting should then be done in
5638 * VBoxSVC.
5639 */
5640HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5641 IN_BSTR aFlags)
5642{
5643 HRESULT rc;
5644
5645 try
5646 {
5647 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5648
5649 BSTR dummy = NULL; /* will not be changed (setter) */
5650 LONG64 dummy64;
5651 if (!directControl)
5652 rc = E_ACCESSDENIED;
5653 else
5654 /** @todo Fix when adding DeleteGuestProperty(),
5655 see defect. */
5656 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5657 true /* isSetter */,
5658 &dummy, &dummy64, &dummy);
5659 }
5660 catch (std::bad_alloc &)
5661 {
5662 rc = E_OUTOFMEMORY;
5663 }
5664
5665 return rc;
5666}
5667#endif // VBOX_WITH_GUEST_PROPS
5668
5669STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5670 IN_BSTR aFlags)
5671{
5672#ifndef VBOX_WITH_GUEST_PROPS
5673 ReturnComNotImplemented();
5674#else // VBOX_WITH_GUEST_PROPS
5675 CheckComArgStrNotEmptyOrNull(aName);
5676 CheckComArgMaybeNull(aFlags);
5677 CheckComArgMaybeNull(aValue);
5678
5679 AutoCaller autoCaller(this);
5680 if (FAILED(autoCaller.rc()))
5681 return autoCaller.rc();
5682
5683 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5684 if (rc == E_ACCESSDENIED)
5685 /* The VM is not running or the service is not (yet) accessible */
5686 rc = setGuestPropertyToService(aName, aValue, aFlags);
5687 return rc;
5688#endif // VBOX_WITH_GUEST_PROPS
5689}
5690
5691STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5692{
5693 return SetGuestProperty(aName, aValue, NULL);
5694}
5695
5696STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5697{
5698 return SetGuestProperty(aName, NULL, NULL);
5699}
5700
5701#ifdef VBOX_WITH_GUEST_PROPS
5702/**
5703 * Enumerate the guest properties in VBoxSVC's internal structures.
5704 */
5705HRESULT Machine::enumerateGuestPropertiesInService
5706 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5707 ComSafeArrayOut(BSTR, aValues),
5708 ComSafeArrayOut(LONG64, aTimestamps),
5709 ComSafeArrayOut(BSTR, aFlags))
5710{
5711 using namespace guestProp;
5712
5713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5714 Utf8Str strPatterns(aPatterns);
5715
5716 /*
5717 * Look for matching patterns and build up a list.
5718 */
5719 HWData::GuestPropertyList propList;
5720 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
5721 it != mHWData->mGuestProperties.end();
5722 ++it)
5723 if ( strPatterns.isEmpty()
5724 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5725 RTSTR_MAX,
5726 it->strName.c_str(),
5727 RTSTR_MAX,
5728 NULL)
5729 )
5730 propList.push_back(*it);
5731
5732 /*
5733 * And build up the arrays for returning the property information.
5734 */
5735 size_t cEntries = propList.size();
5736 SafeArray<BSTR> names(cEntries);
5737 SafeArray<BSTR> values(cEntries);
5738 SafeArray<LONG64> timestamps(cEntries);
5739 SafeArray<BSTR> flags(cEntries);
5740 size_t iProp = 0;
5741 for (HWData::GuestPropertyList::iterator it = propList.begin();
5742 it != propList.end();
5743 ++it)
5744 {
5745 char szFlags[MAX_FLAGS_LEN + 1];
5746 it->strName.cloneTo(&names[iProp]);
5747 it->strValue.cloneTo(&values[iProp]);
5748 timestamps[iProp] = it->mTimestamp;
5749 writeFlags(it->mFlags, szFlags);
5750 Bstr(szFlags).cloneTo(&flags[iProp]);
5751 ++iProp;
5752 }
5753 names.detachTo(ComSafeArrayOutArg(aNames));
5754 values.detachTo(ComSafeArrayOutArg(aValues));
5755 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5756 flags.detachTo(ComSafeArrayOutArg(aFlags));
5757 return S_OK;
5758}
5759
5760/**
5761 * Enumerate the properties managed by a VM.
5762 * @returns E_ACCESSDENIED if the VM process is not available or not
5763 * currently handling queries and the setting should then be done in
5764 * VBoxSVC.
5765 */
5766HRESULT Machine::enumerateGuestPropertiesOnVM
5767 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5768 ComSafeArrayOut(BSTR, aValues),
5769 ComSafeArrayOut(LONG64, aTimestamps),
5770 ComSafeArrayOut(BSTR, aFlags))
5771{
5772 HRESULT rc;
5773 ComPtr<IInternalSessionControl> directControl;
5774 directControl = mData->mSession.mDirectControl;
5775
5776 if (!directControl)
5777 rc = E_ACCESSDENIED;
5778 else
5779 rc = directControl->EnumerateGuestProperties
5780 (aPatterns, ComSafeArrayOutArg(aNames),
5781 ComSafeArrayOutArg(aValues),
5782 ComSafeArrayOutArg(aTimestamps),
5783 ComSafeArrayOutArg(aFlags));
5784 return rc;
5785}
5786#endif // VBOX_WITH_GUEST_PROPS
5787
5788STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5789 ComSafeArrayOut(BSTR, aNames),
5790 ComSafeArrayOut(BSTR, aValues),
5791 ComSafeArrayOut(LONG64, aTimestamps),
5792 ComSafeArrayOut(BSTR, aFlags))
5793{
5794#ifndef VBOX_WITH_GUEST_PROPS
5795 ReturnComNotImplemented();
5796#else // VBOX_WITH_GUEST_PROPS
5797 CheckComArgMaybeNull(aPatterns);
5798 CheckComArgOutSafeArrayPointerValid(aNames);
5799 CheckComArgOutSafeArrayPointerValid(aValues);
5800 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5801 CheckComArgOutSafeArrayPointerValid(aFlags);
5802
5803 AutoCaller autoCaller(this);
5804 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5805
5806 HRESULT rc = enumerateGuestPropertiesOnVM
5807 (aPatterns, ComSafeArrayOutArg(aNames),
5808 ComSafeArrayOutArg(aValues),
5809 ComSafeArrayOutArg(aTimestamps),
5810 ComSafeArrayOutArg(aFlags));
5811 if (rc == E_ACCESSDENIED)
5812 /* The VM is not running or the service is not (yet) accessible */
5813 rc = enumerateGuestPropertiesInService
5814 (aPatterns, ComSafeArrayOutArg(aNames),
5815 ComSafeArrayOutArg(aValues),
5816 ComSafeArrayOutArg(aTimestamps),
5817 ComSafeArrayOutArg(aFlags));
5818 return rc;
5819#endif // VBOX_WITH_GUEST_PROPS
5820}
5821
5822STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5823 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5824{
5825 MediaData::AttachmentList atts;
5826
5827 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5828 if (FAILED(rc)) return rc;
5829
5830 SafeIfaceArray<IMediumAttachment> attachments(atts);
5831 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5832
5833 return S_OK;
5834}
5835
5836STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5837 LONG aControllerPort,
5838 LONG aDevice,
5839 IMediumAttachment **aAttachment)
5840{
5841 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5842 aControllerName, aControllerPort, aDevice));
5843
5844 CheckComArgStrNotEmptyOrNull(aControllerName);
5845 CheckComArgOutPointerValid(aAttachment);
5846
5847 AutoCaller autoCaller(this);
5848 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5849
5850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5851
5852 *aAttachment = NULL;
5853
5854 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
5855 aControllerName,
5856 aControllerPort,
5857 aDevice);
5858 if (pAttach.isNull())
5859 return setError(VBOX_E_OBJECT_NOT_FOUND,
5860 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
5861 aDevice, aControllerPort, aControllerName);
5862
5863 pAttach.queryInterfaceTo(aAttachment);
5864
5865 return S_OK;
5866}
5867
5868STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
5869 StorageBus_T aConnectionType,
5870 IStorageController **controller)
5871{
5872 CheckComArgStrNotEmptyOrNull(aName);
5873
5874 if ( (aConnectionType <= StorageBus_Null)
5875 || (aConnectionType > StorageBus_SAS))
5876 return setError(E_INVALIDARG,
5877 tr("Invalid connection type: %d"),
5878 aConnectionType);
5879
5880 AutoCaller autoCaller(this);
5881 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5882
5883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5884
5885 HRESULT rc = checkStateDependency(MutableStateDep);
5886 if (FAILED(rc)) return rc;
5887
5888 /* try to find one with the name first. */
5889 ComObjPtr<StorageController> ctrl;
5890
5891 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
5892 if (SUCCEEDED(rc))
5893 return setError(VBOX_E_OBJECT_IN_USE,
5894 tr("Storage controller named '%ls' already exists"),
5895 aName);
5896
5897 ctrl.createObject();
5898
5899 /* get a new instance number for the storage controller */
5900 ULONG ulInstance = 0;
5901 bool fBootable = true;
5902 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5903 it != mStorageControllers->end();
5904 ++it)
5905 {
5906 if ((*it)->getStorageBus() == aConnectionType)
5907 {
5908 ULONG ulCurInst = (*it)->getInstance();
5909
5910 if (ulCurInst >= ulInstance)
5911 ulInstance = ulCurInst + 1;
5912
5913 /* Only one controller of each type can be marked as bootable. */
5914 if ((*it)->getBootable())
5915 fBootable = false;
5916 }
5917 }
5918
5919 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5920 if (FAILED(rc)) return rc;
5921
5922 setModified(IsModified_Storage);
5923 mStorageControllers.backup();
5924 mStorageControllers->push_back(ctrl);
5925
5926 ctrl.queryInterfaceTo(controller);
5927
5928 /* inform the direct session if any */
5929 alock.release();
5930 onStorageControllerChange();
5931
5932 return S_OK;
5933}
5934
5935STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
5936 IStorageController **aStorageController)
5937{
5938 CheckComArgStrNotEmptyOrNull(aName);
5939
5940 AutoCaller autoCaller(this);
5941 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5942
5943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5944
5945 ComObjPtr<StorageController> ctrl;
5946
5947 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5948 if (SUCCEEDED(rc))
5949 ctrl.queryInterfaceTo(aStorageController);
5950
5951 return rc;
5952}
5953
5954STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
5955 IStorageController **aStorageController)
5956{
5957 AutoCaller autoCaller(this);
5958 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5959
5960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5961
5962 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5963 it != mStorageControllers->end();
5964 ++it)
5965 {
5966 if ((*it)->getInstance() == aInstance)
5967 {
5968 (*it).queryInterfaceTo(aStorageController);
5969 return S_OK;
5970 }
5971 }
5972
5973 return setError(VBOX_E_OBJECT_NOT_FOUND,
5974 tr("Could not find a storage controller with instance number '%lu'"),
5975 aInstance);
5976}
5977
5978STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
5979{
5980 AutoCaller autoCaller(this);
5981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5982
5983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5984
5985 HRESULT rc = checkStateDependency(MutableStateDep);
5986 if (FAILED(rc)) return rc;
5987
5988 ComObjPtr<StorageController> ctrl;
5989
5990 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
5991 if (SUCCEEDED(rc))
5992 {
5993 /* Ensure that only one controller of each type is marked as bootable. */
5994 if (fBootable == TRUE)
5995 {
5996 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
5997 it != mStorageControllers->end();
5998 ++it)
5999 {
6000 ComObjPtr<StorageController> aCtrl = (*it);
6001
6002 if ( (aCtrl->getName() != Utf8Str(aName))
6003 && aCtrl->getBootable() == TRUE
6004 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6005 && aCtrl->getControllerType() == ctrl->getControllerType())
6006 {
6007 aCtrl->setBootable(FALSE);
6008 break;
6009 }
6010 }
6011 }
6012
6013 if (SUCCEEDED(rc))
6014 {
6015 ctrl->setBootable(fBootable);
6016 setModified(IsModified_Storage);
6017 }
6018 }
6019
6020 if (SUCCEEDED(rc))
6021 {
6022 /* inform the direct session if any */
6023 alock.release();
6024 onStorageControllerChange();
6025 }
6026
6027 return rc;
6028}
6029
6030STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6031{
6032 CheckComArgStrNotEmptyOrNull(aName);
6033
6034 AutoCaller autoCaller(this);
6035 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6036
6037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6038
6039 HRESULT rc = checkStateDependency(MutableStateDep);
6040 if (FAILED(rc)) return rc;
6041
6042 ComObjPtr<StorageController> ctrl;
6043 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6044 if (FAILED(rc)) return rc;
6045
6046 {
6047 /* find all attached devices to the appropriate storage controller and detach them all */
6048 // make a temporary list because detachDevice invalidates iterators into
6049 // mMediaData->mAttachments
6050 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6051
6052 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6053 it != llAttachments2.end();
6054 ++it)
6055 {
6056 MediumAttachment *pAttachTemp = *it;
6057
6058 AutoCaller localAutoCaller(pAttachTemp);
6059 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6060
6061 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6062
6063 if (pAttachTemp->getControllerName() == aName)
6064 {
6065 rc = detachDevice(pAttachTemp, alock, NULL);
6066 if (FAILED(rc)) return rc;
6067 }
6068 }
6069 }
6070
6071 /* We can remove it now. */
6072 setModified(IsModified_Storage);
6073 mStorageControllers.backup();
6074
6075 ctrl->unshare();
6076
6077 mStorageControllers->remove(ctrl);
6078
6079 /* inform the direct session if any */
6080 alock.release();
6081 onStorageControllerChange();
6082
6083 return S_OK;
6084}
6085
6086STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6087 ULONG *puOriginX,
6088 ULONG *puOriginY,
6089 ULONG *puWidth,
6090 ULONG *puHeight,
6091 BOOL *pfEnabled)
6092{
6093 LogFlowThisFunc(("\n"));
6094
6095 CheckComArgNotNull(puOriginX);
6096 CheckComArgNotNull(puOriginY);
6097 CheckComArgNotNull(puWidth);
6098 CheckComArgNotNull(puHeight);
6099 CheckComArgNotNull(pfEnabled);
6100
6101 uint32_t u32OriginX= 0;
6102 uint32_t u32OriginY= 0;
6103 uint32_t u32Width = 0;
6104 uint32_t u32Height = 0;
6105 uint16_t u16Flags = 0;
6106
6107 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6108 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6109 if (RT_FAILURE(vrc))
6110 {
6111#ifdef RT_OS_WINDOWS
6112 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6113 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6114 * So just assign fEnable to TRUE again.
6115 * The right fix would be to change GUI API wrappers to make sure that parameters
6116 * are changed only if API succeeds.
6117 */
6118 *pfEnabled = TRUE;
6119#endif
6120 return setError(VBOX_E_IPRT_ERROR,
6121 tr("Saved guest size is not available (%Rrc)"),
6122 vrc);
6123 }
6124
6125 *puOriginX = u32OriginX;
6126 *puOriginY = u32OriginY;
6127 *puWidth = u32Width;
6128 *puHeight = u32Height;
6129 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6130
6131 return S_OK;
6132}
6133
6134STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6135{
6136 LogFlowThisFunc(("\n"));
6137
6138 CheckComArgNotNull(aSize);
6139 CheckComArgNotNull(aWidth);
6140 CheckComArgNotNull(aHeight);
6141
6142 if (aScreenId != 0)
6143 return E_NOTIMPL;
6144
6145 AutoCaller autoCaller(this);
6146 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6147
6148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6149
6150 uint8_t *pu8Data = NULL;
6151 uint32_t cbData = 0;
6152 uint32_t u32Width = 0;
6153 uint32_t u32Height = 0;
6154
6155 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6156
6157 if (RT_FAILURE(vrc))
6158 return setError(VBOX_E_IPRT_ERROR,
6159 tr("Saved screenshot data is not available (%Rrc)"),
6160 vrc);
6161
6162 *aSize = cbData;
6163 *aWidth = u32Width;
6164 *aHeight = u32Height;
6165
6166 freeSavedDisplayScreenshot(pu8Data);
6167
6168 return S_OK;
6169}
6170
6171STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6172{
6173 LogFlowThisFunc(("\n"));
6174
6175 CheckComArgNotNull(aWidth);
6176 CheckComArgNotNull(aHeight);
6177 CheckComArgOutSafeArrayPointerValid(aData);
6178
6179 if (aScreenId != 0)
6180 return E_NOTIMPL;
6181
6182 AutoCaller autoCaller(this);
6183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6184
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 uint8_t *pu8Data = NULL;
6188 uint32_t cbData = 0;
6189 uint32_t u32Width = 0;
6190 uint32_t u32Height = 0;
6191
6192 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6193
6194 if (RT_FAILURE(vrc))
6195 return setError(VBOX_E_IPRT_ERROR,
6196 tr("Saved screenshot data is not available (%Rrc)"),
6197 vrc);
6198
6199 *aWidth = u32Width;
6200 *aHeight = u32Height;
6201
6202 com::SafeArray<BYTE> bitmap(cbData);
6203 /* Convert pixels to format expected by the API caller. */
6204 if (aBGR)
6205 {
6206 /* [0] B, [1] G, [2] R, [3] A. */
6207 for (unsigned i = 0; i < cbData; i += 4)
6208 {
6209 bitmap[i] = pu8Data[i];
6210 bitmap[i + 1] = pu8Data[i + 1];
6211 bitmap[i + 2] = pu8Data[i + 2];
6212 bitmap[i + 3] = 0xff;
6213 }
6214 }
6215 else
6216 {
6217 /* [0] R, [1] G, [2] B, [3] A. */
6218 for (unsigned i = 0; i < cbData; i += 4)
6219 {
6220 bitmap[i] = pu8Data[i + 2];
6221 bitmap[i + 1] = pu8Data[i + 1];
6222 bitmap[i + 2] = pu8Data[i];
6223 bitmap[i + 3] = 0xff;
6224 }
6225 }
6226 bitmap.detachTo(ComSafeArrayOutArg(aData));
6227
6228 freeSavedDisplayScreenshot(pu8Data);
6229
6230 return S_OK;
6231}
6232
6233
6234STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6235{
6236 LogFlowThisFunc(("\n"));
6237
6238 CheckComArgNotNull(aWidth);
6239 CheckComArgNotNull(aHeight);
6240 CheckComArgOutSafeArrayPointerValid(aData);
6241
6242 if (aScreenId != 0)
6243 return E_NOTIMPL;
6244
6245 AutoCaller autoCaller(this);
6246 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6247
6248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6249
6250 uint8_t *pu8Data = NULL;
6251 uint32_t cbData = 0;
6252 uint32_t u32Width = 0;
6253 uint32_t u32Height = 0;
6254
6255 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6256
6257 if (RT_FAILURE(vrc))
6258 return setError(VBOX_E_IPRT_ERROR,
6259 tr("Saved screenshot data is not available (%Rrc)"),
6260 vrc);
6261
6262 *aWidth = u32Width;
6263 *aHeight = u32Height;
6264
6265 HRESULT rc = S_OK;
6266 uint8_t *pu8PNG = NULL;
6267 uint32_t cbPNG = 0;
6268 uint32_t cxPNG = 0;
6269 uint32_t cyPNG = 0;
6270
6271 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6272
6273 if (RT_SUCCESS(vrc))
6274 {
6275 com::SafeArray<BYTE> screenData(cbPNG);
6276 screenData.initFrom(pu8PNG, cbPNG);
6277 if (pu8PNG)
6278 RTMemFree(pu8PNG);
6279 screenData.detachTo(ComSafeArrayOutArg(aData));
6280 }
6281 else
6282 {
6283 if (pu8PNG)
6284 RTMemFree(pu8PNG);
6285 return setError(VBOX_E_IPRT_ERROR,
6286 tr("Could not convert screenshot to PNG (%Rrc)"),
6287 vrc);
6288 }
6289
6290 freeSavedDisplayScreenshot(pu8Data);
6291
6292 return rc;
6293}
6294
6295STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6296{
6297 LogFlowThisFunc(("\n"));
6298
6299 CheckComArgNotNull(aSize);
6300 CheckComArgNotNull(aWidth);
6301 CheckComArgNotNull(aHeight);
6302
6303 if (aScreenId != 0)
6304 return E_NOTIMPL;
6305
6306 AutoCaller autoCaller(this);
6307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6308
6309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6310
6311 uint8_t *pu8Data = NULL;
6312 uint32_t cbData = 0;
6313 uint32_t u32Width = 0;
6314 uint32_t u32Height = 0;
6315
6316 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6317
6318 if (RT_FAILURE(vrc))
6319 return setError(VBOX_E_IPRT_ERROR,
6320 tr("Saved screenshot data is not available (%Rrc)"),
6321 vrc);
6322
6323 *aSize = cbData;
6324 *aWidth = u32Width;
6325 *aHeight = u32Height;
6326
6327 freeSavedDisplayScreenshot(pu8Data);
6328
6329 return S_OK;
6330}
6331
6332STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6333{
6334 LogFlowThisFunc(("\n"));
6335
6336 CheckComArgNotNull(aWidth);
6337 CheckComArgNotNull(aHeight);
6338 CheckComArgOutSafeArrayPointerValid(aData);
6339
6340 if (aScreenId != 0)
6341 return E_NOTIMPL;
6342
6343 AutoCaller autoCaller(this);
6344 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6345
6346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 uint8_t *pu8Data = NULL;
6349 uint32_t cbData = 0;
6350 uint32_t u32Width = 0;
6351 uint32_t u32Height = 0;
6352
6353 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6354
6355 if (RT_FAILURE(vrc))
6356 return setError(VBOX_E_IPRT_ERROR,
6357 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6358 vrc);
6359
6360 *aWidth = u32Width;
6361 *aHeight = u32Height;
6362
6363 com::SafeArray<BYTE> png(cbData);
6364 png.initFrom(pu8Data, cbData);
6365 png.detachTo(ComSafeArrayOutArg(aData));
6366
6367 freeSavedDisplayScreenshot(pu8Data);
6368
6369 return S_OK;
6370}
6371
6372STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6373{
6374 HRESULT rc = S_OK;
6375 LogFlowThisFunc(("\n"));
6376
6377 AutoCaller autoCaller(this);
6378 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6379
6380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 if (!mHWData->mCPUHotPlugEnabled)
6383 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6384
6385 if (aCpu >= mHWData->mCPUCount)
6386 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6387
6388 if (mHWData->mCPUAttached[aCpu])
6389 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6390
6391 alock.release();
6392 rc = onCPUChange(aCpu, false);
6393 alock.acquire();
6394 if (FAILED(rc)) return rc;
6395
6396 setModified(IsModified_MachineData);
6397 mHWData.backup();
6398 mHWData->mCPUAttached[aCpu] = true;
6399
6400 /* Save settings if online */
6401 if (Global::IsOnline(mData->mMachineState))
6402 saveSettings(NULL);
6403
6404 return S_OK;
6405}
6406
6407STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6408{
6409 HRESULT rc = S_OK;
6410 LogFlowThisFunc(("\n"));
6411
6412 AutoCaller autoCaller(this);
6413 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6414
6415 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6416
6417 if (!mHWData->mCPUHotPlugEnabled)
6418 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6419
6420 if (aCpu >= SchemaDefs::MaxCPUCount)
6421 return setError(E_INVALIDARG,
6422 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6423 SchemaDefs::MaxCPUCount);
6424
6425 if (!mHWData->mCPUAttached[aCpu])
6426 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6427
6428 /* CPU 0 can't be detached */
6429 if (aCpu == 0)
6430 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6431
6432 alock.release();
6433 rc = onCPUChange(aCpu, true);
6434 alock.acquire();
6435 if (FAILED(rc)) return rc;
6436
6437 setModified(IsModified_MachineData);
6438 mHWData.backup();
6439 mHWData->mCPUAttached[aCpu] = false;
6440
6441 /* Save settings if online */
6442 if (Global::IsOnline(mData->mMachineState))
6443 saveSettings(NULL);
6444
6445 return S_OK;
6446}
6447
6448STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6449{
6450 LogFlowThisFunc(("\n"));
6451
6452 CheckComArgNotNull(aCpuAttached);
6453
6454 *aCpuAttached = false;
6455
6456 AutoCaller autoCaller(this);
6457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6458
6459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 /* If hotplug is enabled the CPU is always enabled. */
6462 if (!mHWData->mCPUHotPlugEnabled)
6463 {
6464 if (aCpu < mHWData->mCPUCount)
6465 *aCpuAttached = true;
6466 }
6467 else
6468 {
6469 if (aCpu < SchemaDefs::MaxCPUCount)
6470 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6471 }
6472
6473 return S_OK;
6474}
6475
6476STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6477{
6478 CheckComArgOutPointerValid(aName);
6479
6480 AutoCaller autoCaller(this);
6481 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6482
6483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6484
6485 Utf8Str log = queryLogFilename(aIdx);
6486 if (!RTFileExists(log.c_str()))
6487 log.setNull();
6488 log.cloneTo(aName);
6489
6490 return S_OK;
6491}
6492
6493STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6494{
6495 LogFlowThisFunc(("\n"));
6496 CheckComArgOutSafeArrayPointerValid(aData);
6497 if (aSize < 0)
6498 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6499
6500 AutoCaller autoCaller(this);
6501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6502
6503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6504
6505 HRESULT rc = S_OK;
6506 Utf8Str log = queryLogFilename(aIdx);
6507
6508 /* do not unnecessarily hold the lock while doing something which does
6509 * not need the lock and potentially takes a long time. */
6510 alock.release();
6511
6512 /* Limit the chunk size to 32K for now, as that gives better performance
6513 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6514 * One byte expands to approx. 25 bytes of breathtaking XML. */
6515 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6516 com::SafeArray<BYTE> logData(cbData);
6517
6518 RTFILE LogFile;
6519 int vrc = RTFileOpen(&LogFile, log.c_str(),
6520 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6521 if (RT_SUCCESS(vrc))
6522 {
6523 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6524 if (RT_SUCCESS(vrc))
6525 logData.resize(cbData);
6526 else
6527 rc = setError(VBOX_E_IPRT_ERROR,
6528 tr("Could not read log file '%s' (%Rrc)"),
6529 log.c_str(), vrc);
6530 RTFileClose(LogFile);
6531 }
6532 else
6533 rc = setError(VBOX_E_IPRT_ERROR,
6534 tr("Could not open log file '%s' (%Rrc)"),
6535 log.c_str(), vrc);
6536
6537 if (FAILED(rc))
6538 logData.resize(0);
6539 logData.detachTo(ComSafeArrayOutArg(aData));
6540
6541 return rc;
6542}
6543
6544
6545/**
6546 * Currently this method doesn't attach device to the running VM,
6547 * just makes sure it's plugged on next VM start.
6548 */
6549STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6550{
6551 AutoCaller autoCaller(this);
6552 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6553
6554 // lock scope
6555 {
6556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6557
6558 HRESULT rc = checkStateDependency(MutableStateDep);
6559 if (FAILED(rc)) return rc;
6560
6561 ChipsetType_T aChipset = ChipsetType_PIIX3;
6562 COMGETTER(ChipsetType)(&aChipset);
6563
6564 if (aChipset != ChipsetType_ICH9)
6565 {
6566 return setError(E_INVALIDARG,
6567 tr("Host PCI attachment only supported with ICH9 chipset"));
6568 }
6569
6570 // check if device with this host PCI address already attached
6571 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6572 it != mHWData->mPCIDeviceAssignments.end();
6573 ++it)
6574 {
6575 LONG iHostAddress = -1;
6576 ComPtr<PCIDeviceAttachment> pAttach;
6577 pAttach = *it;
6578 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6579 if (iHostAddress == hostAddress)
6580 return setError(E_INVALIDARG,
6581 tr("Device with host PCI address already attached to this VM"));
6582 }
6583
6584 ComObjPtr<PCIDeviceAttachment> pda;
6585 char name[32];
6586
6587 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6588 Bstr bname(name);
6589 pda.createObject();
6590 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6591 setModified(IsModified_MachineData);
6592 mHWData.backup();
6593 mHWData->mPCIDeviceAssignments.push_back(pda);
6594 }
6595
6596 return S_OK;
6597}
6598
6599/**
6600 * Currently this method doesn't detach device from the running VM,
6601 * just makes sure it's not plugged on next VM start.
6602 */
6603STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6604{
6605 AutoCaller autoCaller(this);
6606 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6607
6608 ComObjPtr<PCIDeviceAttachment> pAttach;
6609 bool fRemoved = false;
6610 HRESULT rc;
6611
6612 // lock scope
6613 {
6614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6615
6616 rc = checkStateDependency(MutableStateDep);
6617 if (FAILED(rc)) return rc;
6618
6619 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6620 it != mHWData->mPCIDeviceAssignments.end();
6621 ++it)
6622 {
6623 LONG iHostAddress = -1;
6624 pAttach = *it;
6625 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6626 if (iHostAddress != -1 && iHostAddress == hostAddress)
6627 {
6628 setModified(IsModified_MachineData);
6629 mHWData.backup();
6630 mHWData->mPCIDeviceAssignments.remove(pAttach);
6631 fRemoved = true;
6632 break;
6633 }
6634 }
6635 }
6636
6637
6638 /* Fire event outside of the lock */
6639 if (fRemoved)
6640 {
6641 Assert(!pAttach.isNull());
6642 ComPtr<IEventSource> es;
6643 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6644 Assert(SUCCEEDED(rc));
6645 Bstr mid;
6646 rc = this->COMGETTER(Id)(mid.asOutParam());
6647 Assert(SUCCEEDED(rc));
6648 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6649 }
6650
6651 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6652 tr("No host PCI device %08x attached"),
6653 hostAddress
6654 );
6655}
6656
6657STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6658{
6659 CheckComArgOutSafeArrayPointerValid(aAssignments);
6660
6661 AutoCaller autoCaller(this);
6662 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6663
6664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6665
6666 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6667 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6668
6669 return S_OK;
6670}
6671
6672STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6673{
6674 CheckComArgOutPointerValid(aBandwidthControl);
6675
6676 AutoCaller autoCaller(this);
6677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6678
6679 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6680
6681 return S_OK;
6682}
6683
6684STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6685{
6686 CheckComArgOutPointerValid(pfEnabled);
6687 AutoCaller autoCaller(this);
6688 HRESULT hrc = autoCaller.rc();
6689 if (SUCCEEDED(hrc))
6690 {
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6693 }
6694 return hrc;
6695}
6696
6697STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6698{
6699 AutoCaller autoCaller(this);
6700 HRESULT hrc = autoCaller.rc();
6701 if (SUCCEEDED(hrc))
6702 {
6703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6704 hrc = checkStateDependency(MutableStateDep);
6705 if (SUCCEEDED(hrc))
6706 {
6707 hrc = mHWData.backupEx();
6708 if (SUCCEEDED(hrc))
6709 {
6710 setModified(IsModified_MachineData);
6711 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6712 }
6713 }
6714 }
6715 return hrc;
6716}
6717
6718STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6719{
6720 CheckComArgOutPointerValid(pbstrConfig);
6721 AutoCaller autoCaller(this);
6722 HRESULT hrc = autoCaller.rc();
6723 if (SUCCEEDED(hrc))
6724 {
6725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6726 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6727 }
6728 return hrc;
6729}
6730
6731STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6732{
6733 CheckComArgStr(bstrConfig);
6734 AutoCaller autoCaller(this);
6735 HRESULT hrc = autoCaller.rc();
6736 if (SUCCEEDED(hrc))
6737 {
6738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6739 hrc = checkStateDependency(MutableStateDep);
6740 if (SUCCEEDED(hrc))
6741 {
6742 hrc = mHWData.backupEx();
6743 if (SUCCEEDED(hrc))
6744 {
6745 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6746 if (SUCCEEDED(hrc))
6747 setModified(IsModified_MachineData);
6748 }
6749 }
6750 }
6751 return hrc;
6752
6753}
6754
6755STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6756{
6757 CheckComArgOutPointerValid(pfAllow);
6758 AutoCaller autoCaller(this);
6759 HRESULT hrc = autoCaller.rc();
6760 if (SUCCEEDED(hrc))
6761 {
6762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6763 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6764 }
6765 return hrc;
6766}
6767
6768STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6769{
6770 AutoCaller autoCaller(this);
6771 HRESULT hrc = autoCaller.rc();
6772 if (SUCCEEDED(hrc))
6773 {
6774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6775 hrc = checkStateDependency(MutableStateDep);
6776 if (SUCCEEDED(hrc))
6777 {
6778 hrc = mHWData.backupEx();
6779 if (SUCCEEDED(hrc))
6780 {
6781 setModified(IsModified_MachineData);
6782 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6783 }
6784 }
6785 }
6786 return hrc;
6787}
6788
6789STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6790{
6791 CheckComArgOutPointerValid(pfEnabled);
6792 AutoCaller autoCaller(this);
6793 HRESULT hrc = autoCaller.rc();
6794 if (SUCCEEDED(hrc))
6795 {
6796 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6797 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6798 }
6799 return hrc;
6800}
6801
6802STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6803{
6804 AutoCaller autoCaller(this);
6805 HRESULT hrc = autoCaller.rc();
6806 if (SUCCEEDED(hrc))
6807 {
6808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6809 hrc = checkStateDependency(MutableStateDep);
6810 if ( SUCCEEDED(hrc)
6811 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6812 {
6813 AutostartDb *autostartDb = mParent->getAutostartDb();
6814 int vrc;
6815
6816 if (fEnabled)
6817 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6818 else
6819 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6820
6821 if (RT_SUCCESS(vrc))
6822 {
6823 hrc = mHWData.backupEx();
6824 if (SUCCEEDED(hrc))
6825 {
6826 setModified(IsModified_MachineData);
6827 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6828 }
6829 }
6830 else if (vrc == VERR_NOT_SUPPORTED)
6831 hrc = setError(VBOX_E_NOT_SUPPORTED,
6832 tr("The VM autostart feature is not supported on this platform"));
6833 else if (vrc == VERR_PATH_NOT_FOUND)
6834 hrc = setError(E_FAIL,
6835 tr("The path to the autostart database is not set"));
6836 else
6837 hrc = setError(E_UNEXPECTED,
6838 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6839 fEnabled ? "Adding" : "Removing",
6840 mUserData->s.strName.c_str(), vrc);
6841 }
6842 }
6843 return hrc;
6844}
6845
6846STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6847{
6848 CheckComArgOutPointerValid(puDelay);
6849 AutoCaller autoCaller(this);
6850 HRESULT hrc = autoCaller.rc();
6851 if (SUCCEEDED(hrc))
6852 {
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 *puDelay = mHWData->mAutostart.uAutostartDelay;
6855 }
6856 return hrc;
6857}
6858
6859STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
6860{
6861 AutoCaller autoCaller(this);
6862 HRESULT hrc = autoCaller.rc();
6863 if (SUCCEEDED(hrc))
6864 {
6865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6866 hrc = checkStateDependency(MutableStateDep);
6867 if (SUCCEEDED(hrc))
6868 {
6869 hrc = mHWData.backupEx();
6870 if (SUCCEEDED(hrc))
6871 {
6872 setModified(IsModified_MachineData);
6873 mHWData->mAutostart.uAutostartDelay = uDelay;
6874 }
6875 }
6876 }
6877 return hrc;
6878}
6879
6880STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
6881{
6882 CheckComArgOutPointerValid(penmAutostopType);
6883 AutoCaller autoCaller(this);
6884 HRESULT hrc = autoCaller.rc();
6885 if (SUCCEEDED(hrc))
6886 {
6887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6888 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
6889 }
6890 return hrc;
6891}
6892
6893STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
6894{
6895 AutoCaller autoCaller(this);
6896 HRESULT hrc = autoCaller.rc();
6897 if (SUCCEEDED(hrc))
6898 {
6899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6900 hrc = checkStateDependency(MutableStateDep);
6901 if ( SUCCEEDED(hrc)
6902 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
6903 {
6904 AutostartDb *autostartDb = mParent->getAutostartDb();
6905 int vrc;
6906
6907 if (enmAutostopType != AutostopType_Disabled)
6908 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6909 else
6910 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6911
6912 if (RT_SUCCESS(vrc))
6913 {
6914 hrc = mHWData.backupEx();
6915 if (SUCCEEDED(hrc))
6916 {
6917 setModified(IsModified_MachineData);
6918 mHWData->mAutostart.enmAutostopType = enmAutostopType;
6919 }
6920 }
6921 else if (vrc == VERR_NOT_SUPPORTED)
6922 hrc = setError(VBOX_E_NOT_SUPPORTED,
6923 tr("The VM autostop feature is not supported on this platform"));
6924 else if (vrc == VERR_PATH_NOT_FOUND)
6925 hrc = setError(E_FAIL,
6926 tr("The path to the autostart database is not set"));
6927 else
6928 hrc = setError(E_UNEXPECTED,
6929 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6930 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6931 mUserData->s.strName.c_str(), vrc);
6932 }
6933 }
6934 return hrc;
6935}
6936
6937
6938STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
6939{
6940 LogFlowFuncEnter();
6941
6942 CheckComArgNotNull(pTarget);
6943 CheckComArgOutPointerValid(pProgress);
6944
6945 /* Convert the options. */
6946 RTCList<CloneOptions_T> optList;
6947 if (options != NULL)
6948 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
6949
6950 if (optList.contains(CloneOptions_Link))
6951 {
6952 if (!isSnapshotMachine())
6953 return setError(E_INVALIDARG,
6954 tr("Linked clone can only be created from a snapshot"));
6955 if (mode != CloneMode_MachineState)
6956 return setError(E_INVALIDARG,
6957 tr("Linked clone can only be created for a single machine state"));
6958 }
6959 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6960
6961 AutoCaller autoCaller(this);
6962 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6963
6964
6965 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
6966
6967 HRESULT rc = pWorker->start(pProgress);
6968
6969 LogFlowFuncLeave();
6970
6971 return rc;
6972}
6973
6974// public methods for internal purposes
6975/////////////////////////////////////////////////////////////////////////////
6976
6977/**
6978 * Adds the given IsModified_* flag to the dirty flags of the machine.
6979 * This must be called either during loadSettings or under the machine write lock.
6980 * @param fl
6981 */
6982void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6983{
6984 mData->flModifications |= fl;
6985 if (fAllowStateModification && isStateModificationAllowed())
6986 mData->mCurrentStateModified = true;
6987}
6988
6989/**
6990 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
6991 * care of the write locking.
6992 *
6993 * @param fModifications The flag to add.
6994 */
6995void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
6996{
6997 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6998 setModified(fModification, fAllowStateModification);
6999}
7000
7001/**
7002 * Saves the registry entry of this machine to the given configuration node.
7003 *
7004 * @param aEntryNode Node to save the registry entry to.
7005 *
7006 * @note locks this object for reading.
7007 */
7008HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7009{
7010 AutoLimitedCaller autoCaller(this);
7011 AssertComRCReturnRC(autoCaller.rc());
7012
7013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7014
7015 data.uuid = mData->mUuid;
7016 data.strSettingsFile = mData->m_strConfigFile;
7017
7018 return S_OK;
7019}
7020
7021/**
7022 * Calculates the absolute path of the given path taking the directory of the
7023 * machine settings file as the current directory.
7024 *
7025 * @param aPath Path to calculate the absolute path for.
7026 * @param aResult Where to put the result (used only on success, can be the
7027 * same Utf8Str instance as passed in @a aPath).
7028 * @return IPRT result.
7029 *
7030 * @note Locks this object for reading.
7031 */
7032int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7033{
7034 AutoCaller autoCaller(this);
7035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7036
7037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7038
7039 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7040
7041 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7042
7043 strSettingsDir.stripFilename();
7044 char folder[RTPATH_MAX];
7045 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7046 if (RT_SUCCESS(vrc))
7047 aResult = folder;
7048
7049 return vrc;
7050}
7051
7052/**
7053 * Copies strSource to strTarget, making it relative to the machine folder
7054 * if it is a subdirectory thereof, or simply copying it otherwise.
7055 *
7056 * @param strSource Path to evaluate and copy.
7057 * @param strTarget Buffer to receive target path.
7058 *
7059 * @note Locks this object for reading.
7060 */
7061void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7062 Utf8Str &strTarget)
7063{
7064 AutoCaller autoCaller(this);
7065 AssertComRCReturn(autoCaller.rc(), (void)0);
7066
7067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7068
7069 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7070 // use strTarget as a temporary buffer to hold the machine settings dir
7071 strTarget = mData->m_strConfigFileFull;
7072 strTarget.stripFilename();
7073 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7074 {
7075 // is relative: then append what's left
7076 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7077 // for empty paths (only possible for subdirs) use "." to avoid
7078 // triggering default settings for not present config attributes.
7079 if (strTarget.isEmpty())
7080 strTarget = ".";
7081 }
7082 else
7083 // is not relative: then overwrite
7084 strTarget = strSource;
7085}
7086
7087/**
7088 * Returns the full path to the machine's log folder in the
7089 * \a aLogFolder argument.
7090 */
7091void Machine::getLogFolder(Utf8Str &aLogFolder)
7092{
7093 AutoCaller autoCaller(this);
7094 AssertComRCReturnVoid(autoCaller.rc());
7095
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 char szTmp[RTPATH_MAX];
7099 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7100 if (RT_SUCCESS(vrc))
7101 {
7102 if (szTmp[0] && !mUserData.isNull())
7103 {
7104 char szTmp2[RTPATH_MAX];
7105 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7106 if (RT_SUCCESS(vrc))
7107 aLogFolder = BstrFmt("%s%c%s",
7108 szTmp2,
7109 RTPATH_DELIMITER,
7110 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7111 }
7112 else
7113 vrc = VERR_PATH_IS_RELATIVE;
7114 }
7115
7116 if (RT_FAILURE(vrc))
7117 {
7118 // fallback if VBOX_USER_LOGHOME is not set or invalid
7119 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7120 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7121 aLogFolder.append(RTPATH_DELIMITER);
7122 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7123 }
7124}
7125
7126/**
7127 * Returns the full path to the machine's log file for an given index.
7128 */
7129Utf8Str Machine::queryLogFilename(ULONG idx)
7130{
7131 Utf8Str logFolder;
7132 getLogFolder(logFolder);
7133 Assert(logFolder.length());
7134 Utf8Str log;
7135 if (idx == 0)
7136 log = Utf8StrFmt("%s%cVBox.log",
7137 logFolder.c_str(), RTPATH_DELIMITER);
7138 else
7139 log = Utf8StrFmt("%s%cVBox.log.%d",
7140 logFolder.c_str(), RTPATH_DELIMITER, idx);
7141 return log;
7142}
7143
7144/**
7145 * Composes a unique saved state filename based on the current system time. The filename is
7146 * granular to the second so this will work so long as no more than one snapshot is taken on
7147 * a machine per second.
7148 *
7149 * Before version 4.1, we used this formula for saved state files:
7150 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7151 * which no longer works because saved state files can now be shared between the saved state of the
7152 * "saved" machine and an online snapshot, and the following would cause problems:
7153 * 1) save machine
7154 * 2) create online snapshot from that machine state --> reusing saved state file
7155 * 3) save machine again --> filename would be reused, breaking the online snapshot
7156 *
7157 * So instead we now use a timestamp.
7158 *
7159 * @param str
7160 */
7161void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturnVoid(autoCaller.rc());
7165
7166 {
7167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7168 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7169 }
7170
7171 RTTIMESPEC ts;
7172 RTTimeNow(&ts);
7173 RTTIME time;
7174 RTTimeExplode(&time, &ts);
7175
7176 strStateFilePath += RTPATH_DELIMITER;
7177 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7178 time.i32Year, time.u8Month, time.u8MonthDay,
7179 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7180}
7181
7182/**
7183 * @note Locks this object for writing, calls the client process
7184 * (inside the lock).
7185 */
7186HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7187 const Utf8Str &strType,
7188 const Utf8Str &strEnvironment,
7189 ProgressProxy *aProgress)
7190{
7191 LogFlowThisFuncEnter();
7192
7193 AssertReturn(aControl, E_FAIL);
7194 AssertReturn(aProgress, E_FAIL);
7195
7196 AutoCaller autoCaller(this);
7197 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7198
7199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7200
7201 if (!mData->mRegistered)
7202 return setError(E_UNEXPECTED,
7203 tr("The machine '%s' is not registered"),
7204 mUserData->s.strName.c_str());
7205
7206 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7207
7208 if ( mData->mSession.mState == SessionState_Locked
7209 || mData->mSession.mState == SessionState_Spawning
7210 || mData->mSession.mState == SessionState_Unlocking)
7211 return setError(VBOX_E_INVALID_OBJECT_STATE,
7212 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7213 mUserData->s.strName.c_str());
7214
7215 /* may not be busy */
7216 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7217
7218 /* get the path to the executable */
7219 char szPath[RTPATH_MAX];
7220 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7221 size_t sz = strlen(szPath);
7222 szPath[sz++] = RTPATH_DELIMITER;
7223 szPath[sz] = 0;
7224 char *cmd = szPath + sz;
7225 sz = RTPATH_MAX - sz;
7226
7227 int vrc = VINF_SUCCESS;
7228 RTPROCESS pid = NIL_RTPROCESS;
7229
7230 RTENV env = RTENV_DEFAULT;
7231
7232 if (!strEnvironment.isEmpty())
7233 {
7234 char *newEnvStr = NULL;
7235
7236 do
7237 {
7238 /* clone the current environment */
7239 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7240 AssertRCBreakStmt(vrc2, vrc = vrc2);
7241
7242 newEnvStr = RTStrDup(strEnvironment.c_str());
7243 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7244
7245 /* put new variables to the environment
7246 * (ignore empty variable names here since RTEnv API
7247 * intentionally doesn't do that) */
7248 char *var = newEnvStr;
7249 for (char *p = newEnvStr; *p; ++p)
7250 {
7251 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7252 {
7253 *p = '\0';
7254 if (*var)
7255 {
7256 char *val = strchr(var, '=');
7257 if (val)
7258 {
7259 *val++ = '\0';
7260 vrc2 = RTEnvSetEx(env, var, val);
7261 }
7262 else
7263 vrc2 = RTEnvUnsetEx(env, var);
7264 if (RT_FAILURE(vrc2))
7265 break;
7266 }
7267 var = p + 1;
7268 }
7269 }
7270 if (RT_SUCCESS(vrc2) && *var)
7271 vrc2 = RTEnvPutEx(env, var);
7272
7273 AssertRCBreakStmt(vrc2, vrc = vrc2);
7274 }
7275 while (0);
7276
7277 if (newEnvStr != NULL)
7278 RTStrFree(newEnvStr);
7279 }
7280
7281 /* Qt is default */
7282#ifdef VBOX_WITH_QTGUI
7283 if (strType == "gui" || strType == "GUI/Qt")
7284 {
7285# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7286 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7287# else
7288 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7289# endif
7290 Assert(sz >= sizeof(VirtualBox_exe));
7291 strcpy(cmd, VirtualBox_exe);
7292
7293 Utf8Str idStr = mData->mUuid.toString();
7294 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7295 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7296 }
7297#else /* !VBOX_WITH_QTGUI */
7298 if (0)
7299 ;
7300#endif /* VBOX_WITH_QTGUI */
7301
7302 else
7303
7304#ifdef VBOX_WITH_VBOXSDL
7305 if (strType == "sdl" || strType == "GUI/SDL")
7306 {
7307 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7308 Assert(sz >= sizeof(VBoxSDL_exe));
7309 strcpy(cmd, VBoxSDL_exe);
7310
7311 Utf8Str idStr = mData->mUuid.toString();
7312 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7313 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7314 }
7315#else /* !VBOX_WITH_VBOXSDL */
7316 if (0)
7317 ;
7318#endif /* !VBOX_WITH_VBOXSDL */
7319
7320 else
7321
7322#ifdef VBOX_WITH_HEADLESS
7323 if ( strType == "headless"
7324 || strType == "capture"
7325 || strType == "vrdp" /* Deprecated. Same as headless. */
7326 )
7327 {
7328 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7329 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7330 * and a VM works even if the server has not been installed.
7331 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7332 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7333 * differently in 4.0 and 3.x.
7334 */
7335 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7336 Assert(sz >= sizeof(VBoxHeadless_exe));
7337 strcpy(cmd, VBoxHeadless_exe);
7338
7339 Utf8Str idStr = mData->mUuid.toString();
7340 /* Leave space for "--capture" arg. */
7341 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7342 "--startvm", idStr.c_str(),
7343 "--vrde", "config",
7344 0, /* For "--capture". */
7345 0 };
7346 if (strType == "capture")
7347 {
7348 unsigned pos = RT_ELEMENTS(args) - 2;
7349 args[pos] = "--capture";
7350 }
7351 vrc = RTProcCreate(szPath, args, env,
7352#ifdef RT_OS_WINDOWS
7353 RTPROC_FLAGS_NO_WINDOW
7354#else
7355 0
7356#endif
7357 , &pid);
7358 }
7359#else /* !VBOX_WITH_HEADLESS */
7360 if (0)
7361 ;
7362#endif /* !VBOX_WITH_HEADLESS */
7363 else
7364 {
7365 RTEnvDestroy(env);
7366 return setError(E_INVALIDARG,
7367 tr("Invalid session type: '%s'"),
7368 strType.c_str());
7369 }
7370
7371 RTEnvDestroy(env);
7372
7373 if (RT_FAILURE(vrc))
7374 return setError(VBOX_E_IPRT_ERROR,
7375 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7376 mUserData->s.strName.c_str(), vrc);
7377
7378 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7379
7380 /*
7381 * Note that we don't release the lock here before calling the client,
7382 * because it doesn't need to call us back if called with a NULL argument.
7383 * Releasing the lock here is dangerous because we didn't prepare the
7384 * launch data yet, but the client we've just started may happen to be
7385 * too fast and call openSession() that will fail (because of PID, etc.),
7386 * so that the Machine will never get out of the Spawning session state.
7387 */
7388
7389 /* inform the session that it will be a remote one */
7390 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7391 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7392 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7393
7394 if (FAILED(rc))
7395 {
7396 /* restore the session state */
7397 mData->mSession.mState = SessionState_Unlocked;
7398 /* The failure may occur w/o any error info (from RPC), so provide one */
7399 return setError(VBOX_E_VM_ERROR,
7400 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7401 }
7402
7403 /* attach launch data to the machine */
7404 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7405 mData->mSession.mRemoteControls.push_back(aControl);
7406 mData->mSession.mProgress = aProgress;
7407 mData->mSession.mPID = pid;
7408 mData->mSession.mState = SessionState_Spawning;
7409 mData->mSession.mType = strType;
7410
7411 LogFlowThisFuncLeave();
7412 return S_OK;
7413}
7414
7415/**
7416 * Returns @c true if the given machine has an open direct session and returns
7417 * the session machine instance and additional session data (on some platforms)
7418 * if so.
7419 *
7420 * Note that when the method returns @c false, the arguments remain unchanged.
7421 *
7422 * @param aMachine Session machine object.
7423 * @param aControl Direct session control object (optional).
7424 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7425 *
7426 * @note locks this object for reading.
7427 */
7428#if defined(RT_OS_WINDOWS)
7429bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7430 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7431 HANDLE *aIPCSem /*= NULL*/,
7432 bool aAllowClosing /*= false*/)
7433#elif defined(RT_OS_OS2)
7434bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7435 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7436 HMTX *aIPCSem /*= NULL*/,
7437 bool aAllowClosing /*= false*/)
7438#else
7439bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7440 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7441 bool aAllowClosing /*= false*/)
7442#endif
7443{
7444 AutoLimitedCaller autoCaller(this);
7445 AssertComRCReturn(autoCaller.rc(), false);
7446
7447 /* just return false for inaccessible machines */
7448 if (autoCaller.state() != Ready)
7449 return false;
7450
7451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7452
7453 if ( mData->mSession.mState == SessionState_Locked
7454 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7455 )
7456 {
7457 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7458
7459 aMachine = mData->mSession.mMachine;
7460
7461 if (aControl != NULL)
7462 *aControl = mData->mSession.mDirectControl;
7463
7464#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7465 /* Additional session data */
7466 if (aIPCSem != NULL)
7467 *aIPCSem = aMachine->mIPCSem;
7468#endif
7469 return true;
7470 }
7471
7472 return false;
7473}
7474
7475/**
7476 * Returns @c true if the given machine has an spawning direct session and
7477 * returns and additional session data (on some platforms) if so.
7478 *
7479 * Note that when the method returns @c false, the arguments remain unchanged.
7480 *
7481 * @param aPID PID of the spawned direct session process.
7482 *
7483 * @note locks this object for reading.
7484 */
7485#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7486bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7487#else
7488bool Machine::isSessionSpawning()
7489#endif
7490{
7491 AutoLimitedCaller autoCaller(this);
7492 AssertComRCReturn(autoCaller.rc(), false);
7493
7494 /* just return false for inaccessible machines */
7495 if (autoCaller.state() != Ready)
7496 return false;
7497
7498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7499
7500 if (mData->mSession.mState == SessionState_Spawning)
7501 {
7502#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7503 /* Additional session data */
7504 if (aPID != NULL)
7505 {
7506 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7507 *aPID = mData->mSession.mPID;
7508 }
7509#endif
7510 return true;
7511 }
7512
7513 return false;
7514}
7515
7516/**
7517 * Called from the client watcher thread to check for unexpected client process
7518 * death during Session_Spawning state (e.g. before it successfully opened a
7519 * direct session).
7520 *
7521 * On Win32 and on OS/2, this method is called only when we've got the
7522 * direct client's process termination notification, so it always returns @c
7523 * true.
7524 *
7525 * On other platforms, this method returns @c true if the client process is
7526 * terminated and @c false if it's still alive.
7527 *
7528 * @note Locks this object for writing.
7529 */
7530bool Machine::checkForSpawnFailure()
7531{
7532 AutoCaller autoCaller(this);
7533 if (!autoCaller.isOk())
7534 {
7535 /* nothing to do */
7536 LogFlowThisFunc(("Already uninitialized!\n"));
7537 return true;
7538 }
7539
7540 /* VirtualBox::addProcessToReap() needs a write lock */
7541 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7542
7543 if (mData->mSession.mState != SessionState_Spawning)
7544 {
7545 /* nothing to do */
7546 LogFlowThisFunc(("Not spawning any more!\n"));
7547 return true;
7548 }
7549
7550 HRESULT rc = S_OK;
7551
7552#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7553
7554 /* the process was already unexpectedly terminated, we just need to set an
7555 * error and finalize session spawning */
7556 rc = setError(E_FAIL,
7557 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7558 getName().c_str());
7559#else
7560
7561 /* PID not yet initialized, skip check. */
7562 if (mData->mSession.mPID == NIL_RTPROCESS)
7563 return false;
7564
7565 RTPROCSTATUS status;
7566 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7567 &status);
7568
7569 if (vrc != VERR_PROCESS_RUNNING)
7570 {
7571 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7572 rc = setError(E_FAIL,
7573 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7574 getName().c_str(), status.iStatus);
7575 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7576 rc = setError(E_FAIL,
7577 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7578 getName().c_str(), status.iStatus);
7579 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7580 rc = setError(E_FAIL,
7581 tr("The virtual machine '%s' has terminated abnormally"),
7582 getName().c_str(), status.iStatus);
7583 else
7584 rc = setError(E_FAIL,
7585 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7586 getName().c_str(), rc);
7587 }
7588
7589#endif
7590
7591 if (FAILED(rc))
7592 {
7593 /* Close the remote session, remove the remote control from the list
7594 * and reset session state to Closed (@note keep the code in sync with
7595 * the relevant part in checkForSpawnFailure()). */
7596
7597 Assert(mData->mSession.mRemoteControls.size() == 1);
7598 if (mData->mSession.mRemoteControls.size() == 1)
7599 {
7600 ErrorInfoKeeper eik;
7601 mData->mSession.mRemoteControls.front()->Uninitialize();
7602 }
7603
7604 mData->mSession.mRemoteControls.clear();
7605 mData->mSession.mState = SessionState_Unlocked;
7606
7607 /* finalize the progress after setting the state */
7608 if (!mData->mSession.mProgress.isNull())
7609 {
7610 mData->mSession.mProgress->notifyComplete(rc);
7611 mData->mSession.mProgress.setNull();
7612 }
7613
7614 mParent->addProcessToReap(mData->mSession.mPID);
7615 mData->mSession.mPID = NIL_RTPROCESS;
7616
7617 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7618 return true;
7619 }
7620
7621 return false;
7622}
7623
7624/**
7625 * Checks whether the machine can be registered. If so, commits and saves
7626 * all settings.
7627 *
7628 * @note Must be called from mParent's write lock. Locks this object and
7629 * children for writing.
7630 */
7631HRESULT Machine::prepareRegister()
7632{
7633 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7634
7635 AutoLimitedCaller autoCaller(this);
7636 AssertComRCReturnRC(autoCaller.rc());
7637
7638 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7639
7640 /* wait for state dependents to drop to zero */
7641 ensureNoStateDependencies();
7642
7643 if (!mData->mAccessible)
7644 return setError(VBOX_E_INVALID_OBJECT_STATE,
7645 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7646 mUserData->s.strName.c_str(),
7647 mData->mUuid.toString().c_str());
7648
7649 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7650
7651 if (mData->mRegistered)
7652 return setError(VBOX_E_INVALID_OBJECT_STATE,
7653 tr("The machine '%s' with UUID {%s} is already registered"),
7654 mUserData->s.strName.c_str(),
7655 mData->mUuid.toString().c_str());
7656
7657 HRESULT rc = S_OK;
7658
7659 // Ensure the settings are saved. If we are going to be registered and
7660 // no config file exists yet, create it by calling saveSettings() too.
7661 if ( (mData->flModifications)
7662 || (!mData->pMachineConfigFile->fileExists())
7663 )
7664 {
7665 rc = saveSettings(NULL);
7666 // no need to check whether VirtualBox.xml needs saving too since
7667 // we can't have a machine XML file rename pending
7668 if (FAILED(rc)) return rc;
7669 }
7670
7671 /* more config checking goes here */
7672
7673 if (SUCCEEDED(rc))
7674 {
7675 /* we may have had implicit modifications we want to fix on success */
7676 commit();
7677
7678 mData->mRegistered = true;
7679 }
7680 else
7681 {
7682 /* we may have had implicit modifications we want to cancel on failure*/
7683 rollback(false /* aNotify */);
7684 }
7685
7686 return rc;
7687}
7688
7689/**
7690 * Increases the number of objects dependent on the machine state or on the
7691 * registered state. Guarantees that these two states will not change at least
7692 * until #releaseStateDependency() is called.
7693 *
7694 * Depending on the @a aDepType value, additional state checks may be made.
7695 * These checks will set extended error info on failure. See
7696 * #checkStateDependency() for more info.
7697 *
7698 * If this method returns a failure, the dependency is not added and the caller
7699 * is not allowed to rely on any particular machine state or registration state
7700 * value and may return the failed result code to the upper level.
7701 *
7702 * @param aDepType Dependency type to add.
7703 * @param aState Current machine state (NULL if not interested).
7704 * @param aRegistered Current registered state (NULL if not interested).
7705 *
7706 * @note Locks this object for writing.
7707 */
7708HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7709 MachineState_T *aState /* = NULL */,
7710 BOOL *aRegistered /* = NULL */)
7711{
7712 AutoCaller autoCaller(this);
7713 AssertComRCReturnRC(autoCaller.rc());
7714
7715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7716
7717 HRESULT rc = checkStateDependency(aDepType);
7718 if (FAILED(rc)) return rc;
7719
7720 {
7721 if (mData->mMachineStateChangePending != 0)
7722 {
7723 /* ensureNoStateDependencies() is waiting for state dependencies to
7724 * drop to zero so don't add more. It may make sense to wait a bit
7725 * and retry before reporting an error (since the pending state
7726 * transition should be really quick) but let's just assert for
7727 * now to see if it ever happens on practice. */
7728
7729 AssertFailed();
7730
7731 return setError(E_ACCESSDENIED,
7732 tr("Machine state change is in progress. Please retry the operation later."));
7733 }
7734
7735 ++mData->mMachineStateDeps;
7736 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7737 }
7738
7739 if (aState)
7740 *aState = mData->mMachineState;
7741 if (aRegistered)
7742 *aRegistered = mData->mRegistered;
7743
7744 return S_OK;
7745}
7746
7747/**
7748 * Decreases the number of objects dependent on the machine state.
7749 * Must always complete the #addStateDependency() call after the state
7750 * dependency is no more necessary.
7751 */
7752void Machine::releaseStateDependency()
7753{
7754 AutoCaller autoCaller(this);
7755 AssertComRCReturnVoid(autoCaller.rc());
7756
7757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7758
7759 /* releaseStateDependency() w/o addStateDependency()? */
7760 AssertReturnVoid(mData->mMachineStateDeps != 0);
7761 -- mData->mMachineStateDeps;
7762
7763 if (mData->mMachineStateDeps == 0)
7764 {
7765 /* inform ensureNoStateDependencies() that there are no more deps */
7766 if (mData->mMachineStateChangePending != 0)
7767 {
7768 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7769 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7770 }
7771 }
7772}
7773
7774// protected methods
7775/////////////////////////////////////////////////////////////////////////////
7776
7777/**
7778 * Performs machine state checks based on the @a aDepType value. If a check
7779 * fails, this method will set extended error info, otherwise it will return
7780 * S_OK. It is supposed, that on failure, the caller will immediately return
7781 * the return value of this method to the upper level.
7782 *
7783 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7784 *
7785 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7786 * current state of this machine object allows to change settings of the
7787 * machine (i.e. the machine is not registered, or registered but not running
7788 * and not saved). It is useful to call this method from Machine setters
7789 * before performing any change.
7790 *
7791 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7792 * as for MutableStateDep except that if the machine is saved, S_OK is also
7793 * returned. This is useful in setters which allow changing machine
7794 * properties when it is in the saved state.
7795 *
7796 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7797 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7798 * Aborted).
7799 *
7800 * @param aDepType Dependency type to check.
7801 *
7802 * @note Non Machine based classes should use #addStateDependency() and
7803 * #releaseStateDependency() methods or the smart AutoStateDependency
7804 * template.
7805 *
7806 * @note This method must be called from under this object's read or write
7807 * lock.
7808 */
7809HRESULT Machine::checkStateDependency(StateDependency aDepType)
7810{
7811 switch (aDepType)
7812 {
7813 case AnyStateDep:
7814 {
7815 break;
7816 }
7817 case MutableStateDep:
7818 {
7819 if ( mData->mRegistered
7820 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7821 || ( mData->mMachineState != MachineState_Paused
7822 && mData->mMachineState != MachineState_Running
7823 && mData->mMachineState != MachineState_Aborted
7824 && mData->mMachineState != MachineState_Teleported
7825 && mData->mMachineState != MachineState_PoweredOff
7826 )
7827 )
7828 )
7829 return setError(VBOX_E_INVALID_VM_STATE,
7830 tr("The machine is not mutable (state is %s)"),
7831 Global::stringifyMachineState(mData->mMachineState));
7832 break;
7833 }
7834 case MutableOrSavedStateDep:
7835 {
7836 if ( mData->mRegistered
7837 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
7838 || ( mData->mMachineState != MachineState_Paused
7839 && mData->mMachineState != MachineState_Running
7840 && mData->mMachineState != MachineState_Aborted
7841 && mData->mMachineState != MachineState_Teleported
7842 && mData->mMachineState != MachineState_Saved
7843 && mData->mMachineState != MachineState_PoweredOff
7844 )
7845 )
7846 )
7847 return setError(VBOX_E_INVALID_VM_STATE,
7848 tr("The machine is not mutable (state is %s)"),
7849 Global::stringifyMachineState(mData->mMachineState));
7850 break;
7851 }
7852 case OfflineStateDep:
7853 {
7854 if ( mData->mRegistered
7855 && ( !isSessionMachine()
7856 || ( mData->mMachineState != MachineState_PoweredOff
7857 && mData->mMachineState != MachineState_Saved
7858 && mData->mMachineState != MachineState_Aborted
7859 && mData->mMachineState != MachineState_Teleported
7860 )
7861 )
7862 )
7863 return setError(VBOX_E_INVALID_VM_STATE,
7864 tr("The machine is not offline (state is %s)"),
7865 Global::stringifyMachineState(mData->mMachineState));
7866 break;
7867 }
7868 }
7869
7870 return S_OK;
7871}
7872
7873/**
7874 * Helper to initialize all associated child objects and allocate data
7875 * structures.
7876 *
7877 * This method must be called as a part of the object's initialization procedure
7878 * (usually done in the #init() method).
7879 *
7880 * @note Must be called only from #init() or from #registeredInit().
7881 */
7882HRESULT Machine::initDataAndChildObjects()
7883{
7884 AutoCaller autoCaller(this);
7885 AssertComRCReturnRC(autoCaller.rc());
7886 AssertComRCReturn(autoCaller.state() == InInit ||
7887 autoCaller.state() == Limited, E_FAIL);
7888
7889 AssertReturn(!mData->mAccessible, E_FAIL);
7890
7891 /* allocate data structures */
7892 mSSData.allocate();
7893 mUserData.allocate();
7894 mHWData.allocate();
7895 mMediaData.allocate();
7896 mStorageControllers.allocate();
7897
7898 /* initialize mOSTypeId */
7899 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
7900
7901 /* create associated BIOS settings object */
7902 unconst(mBIOSSettings).createObject();
7903 mBIOSSettings->init(this);
7904
7905 /* create an associated VRDE object (default is disabled) */
7906 unconst(mVRDEServer).createObject();
7907 mVRDEServer->init(this);
7908
7909 /* create associated serial port objects */
7910 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
7911 {
7912 unconst(mSerialPorts[slot]).createObject();
7913 mSerialPorts[slot]->init(this, slot);
7914 }
7915
7916 /* create associated parallel port objects */
7917 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7918 {
7919 unconst(mParallelPorts[slot]).createObject();
7920 mParallelPorts[slot]->init(this, slot);
7921 }
7922
7923 /* create the audio adapter object (always present, default is disabled) */
7924 unconst(mAudioAdapter).createObject();
7925 mAudioAdapter->init(this);
7926
7927 /* create the USB controller object (always present, default is disabled) */
7928 unconst(mUSBController).createObject();
7929 mUSBController->init(this);
7930
7931 /* create associated network adapter objects */
7932 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
7933 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7934 {
7935 unconst(mNetworkAdapters[slot]).createObject();
7936 mNetworkAdapters[slot]->init(this, slot);
7937 }
7938
7939 /* create the bandwidth control */
7940 unconst(mBandwidthControl).createObject();
7941 mBandwidthControl->init(this);
7942
7943 return S_OK;
7944}
7945
7946/**
7947 * Helper to uninitialize all associated child objects and to free all data
7948 * structures.
7949 *
7950 * This method must be called as a part of the object's uninitialization
7951 * procedure (usually done in the #uninit() method).
7952 *
7953 * @note Must be called only from #uninit() or from #registeredInit().
7954 */
7955void Machine::uninitDataAndChildObjects()
7956{
7957 AutoCaller autoCaller(this);
7958 AssertComRCReturnVoid(autoCaller.rc());
7959 AssertComRCReturnVoid( autoCaller.state() == InUninit
7960 || autoCaller.state() == Limited);
7961
7962 /* tell all our other child objects we've been uninitialized */
7963 if (mBandwidthControl)
7964 {
7965 mBandwidthControl->uninit();
7966 unconst(mBandwidthControl).setNull();
7967 }
7968
7969 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
7970 {
7971 if (mNetworkAdapters[slot])
7972 {
7973 mNetworkAdapters[slot]->uninit();
7974 unconst(mNetworkAdapters[slot]).setNull();
7975 }
7976 }
7977
7978 if (mUSBController)
7979 {
7980 mUSBController->uninit();
7981 unconst(mUSBController).setNull();
7982 }
7983
7984 if (mAudioAdapter)
7985 {
7986 mAudioAdapter->uninit();
7987 unconst(mAudioAdapter).setNull();
7988 }
7989
7990 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
7991 {
7992 if (mParallelPorts[slot])
7993 {
7994 mParallelPorts[slot]->uninit();
7995 unconst(mParallelPorts[slot]).setNull();
7996 }
7997 }
7998
7999 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8000 {
8001 if (mSerialPorts[slot])
8002 {
8003 mSerialPorts[slot]->uninit();
8004 unconst(mSerialPorts[slot]).setNull();
8005 }
8006 }
8007
8008 if (mVRDEServer)
8009 {
8010 mVRDEServer->uninit();
8011 unconst(mVRDEServer).setNull();
8012 }
8013
8014 if (mBIOSSettings)
8015 {
8016 mBIOSSettings->uninit();
8017 unconst(mBIOSSettings).setNull();
8018 }
8019
8020 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine
8021 * instance is uninitialized; SessionMachine instances refer to real
8022 * Machine hard disks). This is necessary for a clean re-initialization of
8023 * the VM after successfully re-checking the accessibility state. Note
8024 * that in case of normal Machine or SnapshotMachine uninitialization (as
8025 * a result of unregistering or deleting the snapshot), outdated hard
8026 * disk attachments will already be uninitialized and deleted, so this
8027 * code will not affect them. */
8028 if ( !!mMediaData
8029 && (!isSessionMachine())
8030 )
8031 {
8032 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8033 it != mMediaData->mAttachments.end();
8034 ++it)
8035 {
8036 ComObjPtr<Medium> hd = (*it)->getMedium();
8037 if (hd.isNull())
8038 continue;
8039 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId());
8040 AssertComRC(rc);
8041 }
8042 }
8043
8044 if (!isSessionMachine() && !isSnapshotMachine())
8045 {
8046 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8047 if (mData->mFirstSnapshot)
8048 {
8049 // snapshots tree is protected by machine write lock; strictly
8050 // this isn't necessary here since we're deleting the entire
8051 // machine, but otherwise we assert in Snapshot::uninit()
8052 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8053 mData->mFirstSnapshot->uninit();
8054 mData->mFirstSnapshot.setNull();
8055 }
8056
8057 mData->mCurrentSnapshot.setNull();
8058 }
8059
8060 /* free data structures (the essential mData structure is not freed here
8061 * since it may be still in use) */
8062 mMediaData.free();
8063 mStorageControllers.free();
8064 mHWData.free();
8065 mUserData.free();
8066 mSSData.free();
8067}
8068
8069/**
8070 * Returns a pointer to the Machine object for this machine that acts like a
8071 * parent for complex machine data objects such as shared folders, etc.
8072 *
8073 * For primary Machine objects and for SnapshotMachine objects, returns this
8074 * object's pointer itself. For SessionMachine objects, returns the peer
8075 * (primary) machine pointer.
8076 */
8077Machine* Machine::getMachine()
8078{
8079 if (isSessionMachine())
8080 return (Machine*)mPeer;
8081 return this;
8082}
8083
8084/**
8085 * Makes sure that there are no machine state dependents. If necessary, waits
8086 * for the number of dependents to drop to zero.
8087 *
8088 * Make sure this method is called from under this object's write lock to
8089 * guarantee that no new dependents may be added when this method returns
8090 * control to the caller.
8091 *
8092 * @note Locks this object for writing. The lock will be released while waiting
8093 * (if necessary).
8094 *
8095 * @warning To be used only in methods that change the machine state!
8096 */
8097void Machine::ensureNoStateDependencies()
8098{
8099 AssertReturnVoid(isWriteLockOnCurrentThread());
8100
8101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8102
8103 /* Wait for all state dependents if necessary */
8104 if (mData->mMachineStateDeps != 0)
8105 {
8106 /* lazy semaphore creation */
8107 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8108 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8109
8110 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8111 mData->mMachineStateDeps));
8112
8113 ++mData->mMachineStateChangePending;
8114
8115 /* reset the semaphore before waiting, the last dependent will signal
8116 * it */
8117 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8118
8119 alock.release();
8120
8121 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8122
8123 alock.acquire();
8124
8125 -- mData->mMachineStateChangePending;
8126 }
8127}
8128
8129/**
8130 * Changes the machine state and informs callbacks.
8131 *
8132 * This method is not intended to fail so it either returns S_OK or asserts (and
8133 * returns a failure).
8134 *
8135 * @note Locks this object for writing.
8136 */
8137HRESULT Machine::setMachineState(MachineState_T aMachineState)
8138{
8139 LogFlowThisFuncEnter();
8140 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8141
8142 AutoCaller autoCaller(this);
8143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8144
8145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8146
8147 /* wait for state dependents to drop to zero */
8148 ensureNoStateDependencies();
8149
8150 if (mData->mMachineState != aMachineState)
8151 {
8152 mData->mMachineState = aMachineState;
8153
8154 RTTimeNow(&mData->mLastStateChange);
8155
8156 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8157 }
8158
8159 LogFlowThisFuncLeave();
8160 return S_OK;
8161}
8162
8163/**
8164 * Searches for a shared folder with the given logical name
8165 * in the collection of shared folders.
8166 *
8167 * @param aName logical name of the shared folder
8168 * @param aSharedFolder where to return the found object
8169 * @param aSetError whether to set the error info if the folder is
8170 * not found
8171 * @return
8172 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8173 *
8174 * @note
8175 * must be called from under the object's lock!
8176 */
8177HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8178 ComObjPtr<SharedFolder> &aSharedFolder,
8179 bool aSetError /* = false */)
8180{
8181 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8182 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8183 it != mHWData->mSharedFolders.end();
8184 ++it)
8185 {
8186 SharedFolder *pSF = *it;
8187 AutoCaller autoCaller(pSF);
8188 if (pSF->getName() == aName)
8189 {
8190 aSharedFolder = pSF;
8191 rc = S_OK;
8192 break;
8193 }
8194 }
8195
8196 if (aSetError && FAILED(rc))
8197 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8198
8199 return rc;
8200}
8201
8202/**
8203 * Initializes all machine instance data from the given settings structures
8204 * from XML. The exception is the machine UUID which needs special handling
8205 * depending on the caller's use case, so the caller needs to set that herself.
8206 *
8207 * This gets called in several contexts during machine initialization:
8208 *
8209 * -- When machine XML exists on disk already and needs to be loaded into memory,
8210 * for example, from registeredInit() to load all registered machines on
8211 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8212 * attached to the machine should be part of some media registry already.
8213 *
8214 * -- During OVF import, when a machine config has been constructed from an
8215 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8216 * ensure that the media listed as attachments in the config (which have
8217 * been imported from the OVF) receive the correct registry ID.
8218 *
8219 * -- During VM cloning.
8220 *
8221 * @param config Machine settings from XML.
8222 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8223 * @return
8224 */
8225HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8226 const Guid *puuidRegistry)
8227{
8228 // copy name, description, OS type, teleporter, UTC etc.
8229 mUserData->s = config.machineUserData;
8230
8231 // look up the object by Id to check it is valid
8232 ComPtr<IGuestOSType> guestOSType;
8233 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8234 guestOSType.asOutParam());
8235 if (FAILED(rc)) return rc;
8236
8237 // stateFile (optional)
8238 if (config.strStateFile.isEmpty())
8239 mSSData->strStateFilePath.setNull();
8240 else
8241 {
8242 Utf8Str stateFilePathFull(config.strStateFile);
8243 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8244 if (RT_FAILURE(vrc))
8245 return setError(E_FAIL,
8246 tr("Invalid saved state file path '%s' (%Rrc)"),
8247 config.strStateFile.c_str(),
8248 vrc);
8249 mSSData->strStateFilePath = stateFilePathFull;
8250 }
8251
8252 // snapshot folder needs special processing so set it again
8253 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8254 if (FAILED(rc)) return rc;
8255
8256 /* Copy the extra data items (Not in any case config is already the same as
8257 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8258 * make sure the extra data map is copied). */
8259 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8260
8261 /* currentStateModified (optional, default is true) */
8262 mData->mCurrentStateModified = config.fCurrentStateModified;
8263
8264 mData->mLastStateChange = config.timeLastStateChange;
8265
8266 /*
8267 * note: all mUserData members must be assigned prior this point because
8268 * we need to commit changes in order to let mUserData be shared by all
8269 * snapshot machine instances.
8270 */
8271 mUserData.commitCopy();
8272
8273 // machine registry, if present (must be loaded before snapshots)
8274 if (config.canHaveOwnMediaRegistry())
8275 {
8276 // determine machine folder
8277 Utf8Str strMachineFolder = getSettingsFileFull();
8278 strMachineFolder.stripFilename();
8279 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8280 config.mediaRegistry,
8281 strMachineFolder);
8282 if (FAILED(rc)) return rc;
8283 }
8284
8285 /* Snapshot node (optional) */
8286 size_t cRootSnapshots;
8287 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8288 {
8289 // there must be only one root snapshot
8290 Assert(cRootSnapshots == 1);
8291
8292 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8293
8294 rc = loadSnapshot(snap,
8295 config.uuidCurrentSnapshot,
8296 NULL); // no parent == first snapshot
8297 if (FAILED(rc)) return rc;
8298 }
8299
8300 // hardware data
8301 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8302 if (FAILED(rc)) return rc;
8303
8304 // load storage controllers
8305 rc = loadStorageControllers(config.storageMachine,
8306 puuidRegistry,
8307 NULL /* puuidSnapshot */);
8308 if (FAILED(rc)) return rc;
8309
8310 /*
8311 * NOTE: the assignment below must be the last thing to do,
8312 * otherwise it will be not possible to change the settings
8313 * somewhere in the code above because all setters will be
8314 * blocked by checkStateDependency(MutableStateDep).
8315 */
8316
8317 /* set the machine state to Aborted or Saved when appropriate */
8318 if (config.fAborted)
8319 {
8320 mSSData->strStateFilePath.setNull();
8321
8322 /* no need to use setMachineState() during init() */
8323 mData->mMachineState = MachineState_Aborted;
8324 }
8325 else if (!mSSData->strStateFilePath.isEmpty())
8326 {
8327 /* no need to use setMachineState() during init() */
8328 mData->mMachineState = MachineState_Saved;
8329 }
8330
8331 // after loading settings, we are no longer different from the XML on disk
8332 mData->flModifications = 0;
8333
8334 return S_OK;
8335}
8336
8337/**
8338 * Recursively loads all snapshots starting from the given.
8339 *
8340 * @param aNode <Snapshot> node.
8341 * @param aCurSnapshotId Current snapshot ID from the settings file.
8342 * @param aParentSnapshot Parent snapshot.
8343 */
8344HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8345 const Guid &aCurSnapshotId,
8346 Snapshot *aParentSnapshot)
8347{
8348 AssertReturn(!isSnapshotMachine(), E_FAIL);
8349 AssertReturn(!isSessionMachine(), E_FAIL);
8350
8351 HRESULT rc = S_OK;
8352
8353 Utf8Str strStateFile;
8354 if (!data.strStateFile.isEmpty())
8355 {
8356 /* optional */
8357 strStateFile = data.strStateFile;
8358 int vrc = calculateFullPath(strStateFile, strStateFile);
8359 if (RT_FAILURE(vrc))
8360 return setError(E_FAIL,
8361 tr("Invalid saved state file path '%s' (%Rrc)"),
8362 strStateFile.c_str(),
8363 vrc);
8364 }
8365
8366 /* create a snapshot machine object */
8367 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8368 pSnapshotMachine.createObject();
8369 rc = pSnapshotMachine->initFromSettings(this,
8370 data.hardware,
8371 &data.debugging,
8372 &data.autostart,
8373 data.storage,
8374 data.uuid.ref(),
8375 strStateFile);
8376 if (FAILED(rc)) return rc;
8377
8378 /* create a snapshot object */
8379 ComObjPtr<Snapshot> pSnapshot;
8380 pSnapshot.createObject();
8381 /* initialize the snapshot */
8382 rc = pSnapshot->init(mParent, // VirtualBox object
8383 data.uuid,
8384 data.strName,
8385 data.strDescription,
8386 data.timestamp,
8387 pSnapshotMachine,
8388 aParentSnapshot);
8389 if (FAILED(rc)) return rc;
8390
8391 /* memorize the first snapshot if necessary */
8392 if (!mData->mFirstSnapshot)
8393 mData->mFirstSnapshot = pSnapshot;
8394
8395 /* memorize the current snapshot when appropriate */
8396 if ( !mData->mCurrentSnapshot
8397 && pSnapshot->getId() == aCurSnapshotId
8398 )
8399 mData->mCurrentSnapshot = pSnapshot;
8400
8401 // now create the children
8402 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8403 it != data.llChildSnapshots.end();
8404 ++it)
8405 {
8406 const settings::Snapshot &childData = *it;
8407 // recurse
8408 rc = loadSnapshot(childData,
8409 aCurSnapshotId,
8410 pSnapshot); // parent = the one we created above
8411 if (FAILED(rc)) return rc;
8412 }
8413
8414 return rc;
8415}
8416
8417/**
8418 * Loads settings into mHWData.
8419 *
8420 * @param data Reference to the hardware settings.
8421 * @param pDbg Pointer to the debugging settings.
8422 * @param pAutostart Pointer to the autostart settings.
8423 */
8424HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8425 const settings::Autostart *pAutostart)
8426{
8427 AssertReturn(!isSessionMachine(), E_FAIL);
8428
8429 HRESULT rc = S_OK;
8430
8431 try
8432 {
8433 /* The hardware version attribute (optional). */
8434 mHWData->mHWVersion = data.strVersion;
8435 mHWData->mHardwareUUID = data.uuid;
8436
8437 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8438 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8439 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8440 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8441 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8442 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8443 mHWData->mPAEEnabled = data.fPAE;
8444 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8445
8446 mHWData->mCPUCount = data.cCPUs;
8447 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8448 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8449
8450 // cpu
8451 if (mHWData->mCPUHotPlugEnabled)
8452 {
8453 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8454 it != data.llCpus.end();
8455 ++it)
8456 {
8457 const settings::Cpu &cpu = *it;
8458
8459 mHWData->mCPUAttached[cpu.ulId] = true;
8460 }
8461 }
8462
8463 // cpuid leafs
8464 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8465 it != data.llCpuIdLeafs.end();
8466 ++it)
8467 {
8468 const settings::CpuIdLeaf &leaf = *it;
8469
8470 switch (leaf.ulId)
8471 {
8472 case 0x0:
8473 case 0x1:
8474 case 0x2:
8475 case 0x3:
8476 case 0x4:
8477 case 0x5:
8478 case 0x6:
8479 case 0x7:
8480 case 0x8:
8481 case 0x9:
8482 case 0xA:
8483 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8484 break;
8485
8486 case 0x80000000:
8487 case 0x80000001:
8488 case 0x80000002:
8489 case 0x80000003:
8490 case 0x80000004:
8491 case 0x80000005:
8492 case 0x80000006:
8493 case 0x80000007:
8494 case 0x80000008:
8495 case 0x80000009:
8496 case 0x8000000A:
8497 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8498 break;
8499
8500 default:
8501 /* just ignore */
8502 break;
8503 }
8504 }
8505
8506 mHWData->mMemorySize = data.ulMemorySizeMB;
8507 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8508
8509 // boot order
8510 for (size_t i = 0;
8511 i < RT_ELEMENTS(mHWData->mBootOrder);
8512 i++)
8513 {
8514 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8515 if (it == data.mapBootOrder.end())
8516 mHWData->mBootOrder[i] = DeviceType_Null;
8517 else
8518 mHWData->mBootOrder[i] = it->second;
8519 }
8520
8521 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8522 mHWData->mMonitorCount = data.cMonitors;
8523 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8524 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8525 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8526 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8527 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8528 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8529 mHWData->mFirmwareType = data.firmwareType;
8530 mHWData->mPointingHIDType = data.pointingHIDType;
8531 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8532 mHWData->mChipsetType = data.chipsetType;
8533 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8534 mHWData->mHPETEnabled = data.fHPETEnabled;
8535
8536 /* VRDEServer */
8537 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8538 if (FAILED(rc)) return rc;
8539
8540 /* BIOS */
8541 rc = mBIOSSettings->loadSettings(data.biosSettings);
8542 if (FAILED(rc)) return rc;
8543
8544 // Bandwidth control (must come before network adapters)
8545 rc = mBandwidthControl->loadSettings(data.ioSettings);
8546 if (FAILED(rc)) return rc;
8547
8548 /* USB Controller */
8549 rc = mUSBController->loadSettings(data.usbController);
8550 if (FAILED(rc)) return rc;
8551
8552 // network adapters
8553 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8554 uint32_t oldCount = mNetworkAdapters.size();
8555 if (newCount > oldCount)
8556 {
8557 mNetworkAdapters.resize(newCount);
8558 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8559 {
8560 unconst(mNetworkAdapters[slot]).createObject();
8561 mNetworkAdapters[slot]->init(this, slot);
8562 }
8563 }
8564 else if (newCount < oldCount)
8565 mNetworkAdapters.resize(newCount);
8566 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8567 it != data.llNetworkAdapters.end();
8568 ++it)
8569 {
8570 const settings::NetworkAdapter &nic = *it;
8571
8572 /* slot unicity is guaranteed by XML Schema */
8573 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8574 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8575 if (FAILED(rc)) return rc;
8576 }
8577
8578 // serial ports
8579 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8580 it != data.llSerialPorts.end();
8581 ++it)
8582 {
8583 const settings::SerialPort &s = *it;
8584
8585 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8586 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8587 if (FAILED(rc)) return rc;
8588 }
8589
8590 // parallel ports (optional)
8591 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8592 it != data.llParallelPorts.end();
8593 ++it)
8594 {
8595 const settings::ParallelPort &p = *it;
8596
8597 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8598 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8599 if (FAILED(rc)) return rc;
8600 }
8601
8602 /* AudioAdapter */
8603 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8604 if (FAILED(rc)) return rc;
8605
8606 /* Shared folders */
8607 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8608 it != data.llSharedFolders.end();
8609 ++it)
8610 {
8611 const settings::SharedFolder &sf = *it;
8612
8613 ComObjPtr<SharedFolder> sharedFolder;
8614 /* Check for double entries. Not allowed! */
8615 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8616 if (SUCCEEDED(rc))
8617 return setError(VBOX_E_OBJECT_IN_USE,
8618 tr("Shared folder named '%s' already exists"),
8619 sf.strName.c_str());
8620
8621 /* Create the new shared folder. Don't break on error. This will be
8622 * reported when the machine starts. */
8623 sharedFolder.createObject();
8624 rc = sharedFolder->init(getMachine(),
8625 sf.strName,
8626 sf.strHostPath,
8627 RT_BOOL(sf.fWritable),
8628 RT_BOOL(sf.fAutoMount),
8629 false /* fFailOnError */);
8630 if (FAILED(rc)) return rc;
8631 mHWData->mSharedFolders.push_back(sharedFolder);
8632 }
8633
8634 // Clipboard
8635 mHWData->mClipboardMode = data.clipboardMode;
8636
8637 // drag'n'drop
8638 mHWData->mDragAndDropMode = data.dragAndDropMode;
8639
8640 // guest settings
8641 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8642
8643 // IO settings
8644 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8645 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8646
8647 // Host PCI devices
8648 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8649 it != data.pciAttachments.end();
8650 ++it)
8651 {
8652 const settings::HostPCIDeviceAttachment &hpda = *it;
8653 ComObjPtr<PCIDeviceAttachment> pda;
8654
8655 pda.createObject();
8656 pda->loadSettings(this, hpda);
8657 mHWData->mPCIDeviceAssignments.push_back(pda);
8658 }
8659
8660 /*
8661 * (The following isn't really real hardware, but it lives in HWData
8662 * for reasons of convenience.)
8663 */
8664
8665#ifdef VBOX_WITH_GUEST_PROPS
8666 /* Guest properties (optional) */
8667 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8668 it != data.llGuestProperties.end();
8669 ++it)
8670 {
8671 const settings::GuestProperty &prop = *it;
8672 uint32_t fFlags = guestProp::NILFLAG;
8673 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8674 HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags };
8675 mHWData->mGuestProperties.push_back(property);
8676 }
8677
8678 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8679#endif /* VBOX_WITH_GUEST_PROPS defined */
8680
8681 rc = loadDebugging(pDbg);
8682 if (FAILED(rc))
8683 return rc;
8684
8685 mHWData->mAutostart = *pAutostart;
8686 }
8687 catch(std::bad_alloc &)
8688 {
8689 return E_OUTOFMEMORY;
8690 }
8691
8692 AssertComRC(rc);
8693 return rc;
8694}
8695
8696/**
8697 * Called from Machine::loadHardware() to load the debugging settings of the
8698 * machine.
8699 *
8700 * @param pDbg Pointer to the settings.
8701 */
8702HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8703{
8704 mHWData->mDebugging = *pDbg;
8705 /* no more processing currently required, this will probably change. */
8706 return S_OK;
8707}
8708
8709/**
8710 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8711 *
8712 * @param data
8713 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8714 * @param puuidSnapshot
8715 * @return
8716 */
8717HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8718 const Guid *puuidRegistry,
8719 const Guid *puuidSnapshot)
8720{
8721 AssertReturn(!isSessionMachine(), E_FAIL);
8722
8723 HRESULT rc = S_OK;
8724
8725 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8726 it != data.llStorageControllers.end();
8727 ++it)
8728 {
8729 const settings::StorageController &ctlData = *it;
8730
8731 ComObjPtr<StorageController> pCtl;
8732 /* Try to find one with the name first. */
8733 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8734 if (SUCCEEDED(rc))
8735 return setError(VBOX_E_OBJECT_IN_USE,
8736 tr("Storage controller named '%s' already exists"),
8737 ctlData.strName.c_str());
8738
8739 pCtl.createObject();
8740 rc = pCtl->init(this,
8741 ctlData.strName,
8742 ctlData.storageBus,
8743 ctlData.ulInstance,
8744 ctlData.fBootable);
8745 if (FAILED(rc)) return rc;
8746
8747 mStorageControllers->push_back(pCtl);
8748
8749 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8750 if (FAILED(rc)) return rc;
8751
8752 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8753 if (FAILED(rc)) return rc;
8754
8755 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8756 if (FAILED(rc)) return rc;
8757
8758 /* Set IDE emulation settings (only for AHCI controller). */
8759 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8760 {
8761 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8762 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8763 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8764 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8765 )
8766 return rc;
8767 }
8768
8769 /* Load the attached devices now. */
8770 rc = loadStorageDevices(pCtl,
8771 ctlData,
8772 puuidRegistry,
8773 puuidSnapshot);
8774 if (FAILED(rc)) return rc;
8775 }
8776
8777 return S_OK;
8778}
8779
8780/**
8781 * Called from loadStorageControllers for a controller's devices.
8782 *
8783 * @param aStorageController
8784 * @param data
8785 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8786 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8787 * @return
8788 */
8789HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8790 const settings::StorageController &data,
8791 const Guid *puuidRegistry,
8792 const Guid *puuidSnapshot)
8793{
8794 HRESULT rc = S_OK;
8795
8796 /* paranoia: detect duplicate attachments */
8797 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8798 it != data.llAttachedDevices.end();
8799 ++it)
8800 {
8801 const settings::AttachedDevice &ad = *it;
8802
8803 for (settings::AttachedDevicesList::const_iterator it2 = it;
8804 it2 != data.llAttachedDevices.end();
8805 ++it2)
8806 {
8807 if (it == it2)
8808 continue;
8809
8810 const settings::AttachedDevice &ad2 = *it2;
8811
8812 if ( ad.lPort == ad2.lPort
8813 && ad.lDevice == ad2.lDevice)
8814 {
8815 return setError(E_FAIL,
8816 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
8817 aStorageController->getName().c_str(),
8818 ad.lPort,
8819 ad.lDevice,
8820 mUserData->s.strName.c_str());
8821 }
8822 }
8823 }
8824
8825 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
8826 it != data.llAttachedDevices.end();
8827 ++it)
8828 {
8829 const settings::AttachedDevice &dev = *it;
8830 ComObjPtr<Medium> medium;
8831
8832 switch (dev.deviceType)
8833 {
8834 case DeviceType_Floppy:
8835 case DeviceType_DVD:
8836 if (dev.strHostDriveSrc.isNotEmpty())
8837 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
8838 else
8839 rc = mParent->findRemoveableMedium(dev.deviceType,
8840 dev.uuid,
8841 false /* fRefresh */,
8842 false /* aSetError */,
8843 medium);
8844 if (rc == VBOX_E_OBJECT_NOT_FOUND)
8845 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
8846 rc = S_OK;
8847 break;
8848
8849 case DeviceType_HardDisk:
8850 {
8851 /* find a hard disk by UUID */
8852 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
8853 if (FAILED(rc))
8854 {
8855 if (isSnapshotMachine())
8856 {
8857 // wrap another error message around the "cannot find hard disk" set by findHardDisk
8858 // so the user knows that the bad disk is in a snapshot somewhere
8859 com::ErrorInfo info;
8860 return setError(E_FAIL,
8861 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
8862 puuidSnapshot->raw(),
8863 info.getText().raw());
8864 }
8865 else
8866 return rc;
8867 }
8868
8869 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
8870
8871 if (medium->getType() == MediumType_Immutable)
8872 {
8873 if (isSnapshotMachine())
8874 return setError(E_FAIL,
8875 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8876 "of the virtual machine '%s' ('%s')"),
8877 medium->getLocationFull().c_str(),
8878 dev.uuid.raw(),
8879 puuidSnapshot->raw(),
8880 mUserData->s.strName.c_str(),
8881 mData->m_strConfigFileFull.c_str());
8882
8883 return setError(E_FAIL,
8884 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8885 medium->getLocationFull().c_str(),
8886 dev.uuid.raw(),
8887 mUserData->s.strName.c_str(),
8888 mData->m_strConfigFileFull.c_str());
8889 }
8890
8891 if (medium->getType() == MediumType_MultiAttach)
8892 {
8893 if (isSnapshotMachine())
8894 return setError(E_FAIL,
8895 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
8896 "of the virtual machine '%s' ('%s')"),
8897 medium->getLocationFull().c_str(),
8898 dev.uuid.raw(),
8899 puuidSnapshot->raw(),
8900 mUserData->s.strName.c_str(),
8901 mData->m_strConfigFileFull.c_str());
8902
8903 return setError(E_FAIL,
8904 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
8905 medium->getLocationFull().c_str(),
8906 dev.uuid.raw(),
8907 mUserData->s.strName.c_str(),
8908 mData->m_strConfigFileFull.c_str());
8909 }
8910
8911 if ( !isSnapshotMachine()
8912 && medium->getChildren().size() != 0
8913 )
8914 return setError(E_FAIL,
8915 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
8916 "because it has %d differencing child hard disks"),
8917 medium->getLocationFull().c_str(),
8918 dev.uuid.raw(),
8919 mUserData->s.strName.c_str(),
8920 mData->m_strConfigFileFull.c_str(),
8921 medium->getChildren().size());
8922
8923 if (findAttachment(mMediaData->mAttachments,
8924 medium))
8925 return setError(E_FAIL,
8926 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
8927 medium->getLocationFull().c_str(),
8928 dev.uuid.raw(),
8929 mUserData->s.strName.c_str(),
8930 mData->m_strConfigFileFull.c_str());
8931
8932 break;
8933 }
8934
8935 default:
8936 return setError(E_FAIL,
8937 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
8938 medium->getLocationFull().c_str(),
8939 mUserData->s.strName.c_str(),
8940 mData->m_strConfigFileFull.c_str());
8941 }
8942
8943 if (FAILED(rc))
8944 break;
8945
8946 /* Bandwidth groups are loaded at this point. */
8947 ComObjPtr<BandwidthGroup> pBwGroup;
8948
8949 if (!dev.strBwGroup.isEmpty())
8950 {
8951 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
8952 if (FAILED(rc))
8953 return setError(E_FAIL,
8954 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
8955 medium->getLocationFull().c_str(),
8956 dev.strBwGroup.c_str(),
8957 mUserData->s.strName.c_str(),
8958 mData->m_strConfigFileFull.c_str());
8959 pBwGroup->reference();
8960 }
8961
8962 const Bstr controllerName = aStorageController->getName();
8963 ComObjPtr<MediumAttachment> pAttachment;
8964 pAttachment.createObject();
8965 rc = pAttachment->init(this,
8966 medium,
8967 controllerName,
8968 dev.lPort,
8969 dev.lDevice,
8970 dev.deviceType,
8971 false,
8972 dev.fPassThrough,
8973 dev.fTempEject,
8974 dev.fNonRotational,
8975 dev.fDiscard,
8976 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
8977 if (FAILED(rc)) break;
8978
8979 /* associate the medium with this machine and snapshot */
8980 if (!medium.isNull())
8981 {
8982 AutoCaller medCaller(medium);
8983 if (FAILED(medCaller.rc())) return medCaller.rc();
8984 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
8985
8986 if (isSnapshotMachine())
8987 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
8988 else
8989 rc = medium->addBackReference(mData->mUuid);
8990 /* If the medium->addBackReference fails it sets an appropriate
8991 * error message, so no need to do any guesswork here. */
8992
8993 if (puuidRegistry)
8994 // caller wants registry ID to be set on all attached media (OVF import case)
8995 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
8996 }
8997
8998 if (FAILED(rc))
8999 break;
9000
9001 /* back up mMediaData to let registeredInit() properly rollback on failure
9002 * (= limited accessibility) */
9003 setModified(IsModified_Storage);
9004 mMediaData.backup();
9005 mMediaData->mAttachments.push_back(pAttachment);
9006 }
9007
9008 return rc;
9009}
9010
9011/**
9012 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9013 *
9014 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9015 * @param aSnapshot where to return the found snapshot
9016 * @param aSetError true to set extended error info on failure
9017 */
9018HRESULT Machine::findSnapshotById(const Guid &aId,
9019 ComObjPtr<Snapshot> &aSnapshot,
9020 bool aSetError /* = false */)
9021{
9022 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9023
9024 if (!mData->mFirstSnapshot)
9025 {
9026 if (aSetError)
9027 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9028 return E_FAIL;
9029 }
9030
9031 if (aId.isEmpty())
9032 aSnapshot = mData->mFirstSnapshot;
9033 else
9034 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9035
9036 if (!aSnapshot)
9037 {
9038 if (aSetError)
9039 return setError(E_FAIL,
9040 tr("Could not find a snapshot with UUID {%s}"),
9041 aId.toString().c_str());
9042 return E_FAIL;
9043 }
9044
9045 return S_OK;
9046}
9047
9048/**
9049 * Returns the snapshot with the given name or fails of no such snapshot.
9050 *
9051 * @param aName snapshot name to find
9052 * @param aSnapshot where to return the found snapshot
9053 * @param aSetError true to set extended error info on failure
9054 */
9055HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9056 ComObjPtr<Snapshot> &aSnapshot,
9057 bool aSetError /* = false */)
9058{
9059 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9060
9061 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9062
9063 if (!mData->mFirstSnapshot)
9064 {
9065 if (aSetError)
9066 return setError(VBOX_E_OBJECT_NOT_FOUND,
9067 tr("This machine does not have any snapshots"));
9068 return VBOX_E_OBJECT_NOT_FOUND;
9069 }
9070
9071 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9072
9073 if (!aSnapshot)
9074 {
9075 if (aSetError)
9076 return setError(VBOX_E_OBJECT_NOT_FOUND,
9077 tr("Could not find a snapshot named '%s'"), strName.c_str());
9078 return VBOX_E_OBJECT_NOT_FOUND;
9079 }
9080
9081 return S_OK;
9082}
9083
9084/**
9085 * Returns a storage controller object with the given name.
9086 *
9087 * @param aName storage controller name to find
9088 * @param aStorageController where to return the found storage controller
9089 * @param aSetError true to set extended error info on failure
9090 */
9091HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9092 ComObjPtr<StorageController> &aStorageController,
9093 bool aSetError /* = false */)
9094{
9095 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9096
9097 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9098 it != mStorageControllers->end();
9099 ++it)
9100 {
9101 if ((*it)->getName() == aName)
9102 {
9103 aStorageController = (*it);
9104 return S_OK;
9105 }
9106 }
9107
9108 if (aSetError)
9109 return setError(VBOX_E_OBJECT_NOT_FOUND,
9110 tr("Could not find a storage controller named '%s'"),
9111 aName.c_str());
9112 return VBOX_E_OBJECT_NOT_FOUND;
9113}
9114
9115HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9116 MediaData::AttachmentList &atts)
9117{
9118 AutoCaller autoCaller(this);
9119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9120
9121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9122
9123 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9124 it != mMediaData->mAttachments.end();
9125 ++it)
9126 {
9127 const ComObjPtr<MediumAttachment> &pAtt = *it;
9128
9129 // should never happen, but deal with NULL pointers in the list.
9130 AssertStmt(!pAtt.isNull(), continue);
9131
9132 // getControllerName() needs caller+read lock
9133 AutoCaller autoAttCaller(pAtt);
9134 if (FAILED(autoAttCaller.rc()))
9135 {
9136 atts.clear();
9137 return autoAttCaller.rc();
9138 }
9139 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9140
9141 if (pAtt->getControllerName() == aName)
9142 atts.push_back(pAtt);
9143 }
9144
9145 return S_OK;
9146}
9147
9148/**
9149 * Helper for #saveSettings. Cares about renaming the settings directory and
9150 * file if the machine name was changed and about creating a new settings file
9151 * if this is a new machine.
9152 *
9153 * @note Must be never called directly but only from #saveSettings().
9154 */
9155HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9156{
9157 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9158
9159 HRESULT rc = S_OK;
9160
9161 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9162
9163 /// @todo need to handle primary group change, too
9164
9165 /* attempt to rename the settings file if machine name is changed */
9166 if ( mUserData->s.fNameSync
9167 && mUserData.isBackedUp()
9168 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9169 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9170 )
9171 {
9172 bool dirRenamed = false;
9173 bool fileRenamed = false;
9174
9175 Utf8Str configFile, newConfigFile;
9176 Utf8Str configFilePrev, newConfigFilePrev;
9177 Utf8Str configDir, newConfigDir;
9178
9179 do
9180 {
9181 int vrc = VINF_SUCCESS;
9182
9183 Utf8Str name = mUserData.backedUpData()->s.strName;
9184 Utf8Str newName = mUserData->s.strName;
9185 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9186 if (group == "/")
9187 group.setNull();
9188 Utf8Str newGroup = mUserData->s.llGroups.front();
9189 if (newGroup == "/")
9190 newGroup.setNull();
9191
9192 configFile = mData->m_strConfigFileFull;
9193
9194 /* first, rename the directory if it matches the group and machine name */
9195 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9196 group.c_str(), RTPATH_DELIMITER, name.c_str());
9197 /** @todo hack, make somehow use of ComposeMachineFilename */
9198 if (mUserData->s.fDirectoryIncludesUUID)
9199 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9200 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9201 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9202 /** @todo hack, make somehow use of ComposeMachineFilename */
9203 if (mUserData->s.fDirectoryIncludesUUID)
9204 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9205 configDir = configFile;
9206 configDir.stripFilename();
9207 newConfigDir = configDir;
9208 if ( configDir.length() >= groupPlusName.length()
9209 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9210 {
9211 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9212 Utf8Str newConfigBaseDir(newConfigDir);
9213 newConfigDir.append(newGroupPlusName);
9214 /* consistency: use \ if appropriate on the platform */
9215 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9216 /* new dir and old dir cannot be equal here because of 'if'
9217 * above and because name != newName */
9218 Assert(configDir != newConfigDir);
9219 if (!fSettingsFileIsNew)
9220 {
9221 /* perform real rename only if the machine is not new */
9222 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9223 if ( vrc == VERR_FILE_NOT_FOUND
9224 || vrc == VERR_PATH_NOT_FOUND)
9225 {
9226 /* create the parent directory, then retry renaming */
9227 Utf8Str parent(newConfigDir);
9228 parent.stripFilename();
9229 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9230 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9231 }
9232 if (RT_FAILURE(vrc))
9233 {
9234 rc = setError(E_FAIL,
9235 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9236 configDir.c_str(),
9237 newConfigDir.c_str(),
9238 vrc);
9239 break;
9240 }
9241 /* delete subdirectories which are no longer needed */
9242 Utf8Str dir(configDir);
9243 dir.stripFilename();
9244 while (dir != newConfigBaseDir && dir != ".")
9245 {
9246 vrc = RTDirRemove(dir.c_str());
9247 if (RT_FAILURE(vrc))
9248 break;
9249 dir.stripFilename();
9250 }
9251 dirRenamed = true;
9252 }
9253 }
9254
9255 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9256 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9257
9258 /* then try to rename the settings file itself */
9259 if (newConfigFile != configFile)
9260 {
9261 /* get the path to old settings file in renamed directory */
9262 configFile = Utf8StrFmt("%s%c%s",
9263 newConfigDir.c_str(),
9264 RTPATH_DELIMITER,
9265 RTPathFilename(configFile.c_str()));
9266 if (!fSettingsFileIsNew)
9267 {
9268 /* perform real rename only if the machine is not new */
9269 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9270 if (RT_FAILURE(vrc))
9271 {
9272 rc = setError(E_FAIL,
9273 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9274 configFile.c_str(),
9275 newConfigFile.c_str(),
9276 vrc);
9277 break;
9278 }
9279 fileRenamed = true;
9280 configFilePrev = configFile;
9281 configFilePrev += "-prev";
9282 newConfigFilePrev = newConfigFile;
9283 newConfigFilePrev += "-prev";
9284 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9285 }
9286 }
9287
9288 // update m_strConfigFileFull amd mConfigFile
9289 mData->m_strConfigFileFull = newConfigFile;
9290 // compute the relative path too
9291 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9292
9293 // store the old and new so that VirtualBox::saveSettings() can update
9294 // the media registry
9295 if ( mData->mRegistered
9296 && configDir != newConfigDir)
9297 {
9298 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9299
9300 if (pfNeedsGlobalSaveSettings)
9301 *pfNeedsGlobalSaveSettings = true;
9302 }
9303
9304 // in the saved state file path, replace the old directory with the new directory
9305 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9306 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9307
9308 // and do the same thing for the saved state file paths of all the online snapshots
9309 if (mData->mFirstSnapshot)
9310 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9311 newConfigDir.c_str());
9312 }
9313 while (0);
9314
9315 if (FAILED(rc))
9316 {
9317 /* silently try to rename everything back */
9318 if (fileRenamed)
9319 {
9320 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9321 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9322 }
9323 if (dirRenamed)
9324 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9325 }
9326
9327 if (FAILED(rc)) return rc;
9328 }
9329
9330 if (fSettingsFileIsNew)
9331 {
9332 /* create a virgin config file */
9333 int vrc = VINF_SUCCESS;
9334
9335 /* ensure the settings directory exists */
9336 Utf8Str path(mData->m_strConfigFileFull);
9337 path.stripFilename();
9338 if (!RTDirExists(path.c_str()))
9339 {
9340 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9341 if (RT_FAILURE(vrc))
9342 {
9343 return setError(E_FAIL,
9344 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9345 path.c_str(),
9346 vrc);
9347 }
9348 }
9349
9350 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9351 path = Utf8Str(mData->m_strConfigFileFull);
9352 RTFILE f = NIL_RTFILE;
9353 vrc = RTFileOpen(&f, path.c_str(),
9354 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9355 if (RT_FAILURE(vrc))
9356 return setError(E_FAIL,
9357 tr("Could not create the settings file '%s' (%Rrc)"),
9358 path.c_str(),
9359 vrc);
9360 RTFileClose(f);
9361 }
9362
9363 return rc;
9364}
9365
9366/**
9367 * Saves and commits machine data, user data and hardware data.
9368 *
9369 * Note that on failure, the data remains uncommitted.
9370 *
9371 * @a aFlags may combine the following flags:
9372 *
9373 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9374 * Used when saving settings after an operation that makes them 100%
9375 * correspond to the settings from the current snapshot.
9376 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9377 * #isReallyModified() returns false. This is necessary for cases when we
9378 * change machine data directly, not through the backup()/commit() mechanism.
9379 * - SaveS_Force: settings will be saved without doing a deep compare of the
9380 * settings structures. This is used when this is called because snapshots
9381 * have changed to avoid the overhead of the deep compare.
9382 *
9383 * @note Must be called from under this object's write lock. Locks children for
9384 * writing.
9385 *
9386 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9387 * initialized to false and that will be set to true by this function if
9388 * the caller must invoke VirtualBox::saveSettings() because the global
9389 * settings have changed. This will happen if a machine rename has been
9390 * saved and the global machine and media registries will therefore need
9391 * updating.
9392 */
9393HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9394 int aFlags /*= 0*/)
9395{
9396 LogFlowThisFuncEnter();
9397
9398 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9399
9400 /* make sure child objects are unable to modify the settings while we are
9401 * saving them */
9402 ensureNoStateDependencies();
9403
9404 AssertReturn(!isSnapshotMachine(),
9405 E_FAIL);
9406
9407 HRESULT rc = S_OK;
9408 bool fNeedsWrite = false;
9409
9410 /* First, prepare to save settings. It will care about renaming the
9411 * settings directory and file if the machine name was changed and about
9412 * creating a new settings file if this is a new machine. */
9413 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9414 if (FAILED(rc)) return rc;
9415
9416 // keep a pointer to the current settings structures
9417 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9418 settings::MachineConfigFile *pNewConfig = NULL;
9419
9420 try
9421 {
9422 // make a fresh one to have everyone write stuff into
9423 pNewConfig = new settings::MachineConfigFile(NULL);
9424 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9425
9426 // now go and copy all the settings data from COM to the settings structures
9427 // (this calles saveSettings() on all the COM objects in the machine)
9428 copyMachineDataToSettings(*pNewConfig);
9429
9430 if (aFlags & SaveS_ResetCurStateModified)
9431 {
9432 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9433 mData->mCurrentStateModified = FALSE;
9434 fNeedsWrite = true; // always, no need to compare
9435 }
9436 else if (aFlags & SaveS_Force)
9437 {
9438 fNeedsWrite = true; // always, no need to compare
9439 }
9440 else
9441 {
9442 if (!mData->mCurrentStateModified)
9443 {
9444 // do a deep compare of the settings that we just saved with the settings
9445 // previously stored in the config file; this invokes MachineConfigFile::operator==
9446 // which does a deep compare of all the settings, which is expensive but less expensive
9447 // than writing out XML in vain
9448 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9449
9450 // could still be modified if any settings changed
9451 mData->mCurrentStateModified = fAnySettingsChanged;
9452
9453 fNeedsWrite = fAnySettingsChanged;
9454 }
9455 else
9456 fNeedsWrite = true;
9457 }
9458
9459 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9460
9461 if (fNeedsWrite)
9462 // now spit it all out!
9463 pNewConfig->write(mData->m_strConfigFileFull);
9464
9465 mData->pMachineConfigFile = pNewConfig;
9466 delete pOldConfig;
9467 commit();
9468
9469 // after saving settings, we are no longer different from the XML on disk
9470 mData->flModifications = 0;
9471 }
9472 catch (HRESULT err)
9473 {
9474 // we assume that error info is set by the thrower
9475 rc = err;
9476
9477 // restore old config
9478 delete pNewConfig;
9479 mData->pMachineConfigFile = pOldConfig;
9480 }
9481 catch (...)
9482 {
9483 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9484 }
9485
9486 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9487 {
9488 /* Fire the data change event, even on failure (since we've already
9489 * committed all data). This is done only for SessionMachines because
9490 * mutable Machine instances are always not registered (i.e. private
9491 * to the client process that creates them) and thus don't need to
9492 * inform callbacks. */
9493 if (isSessionMachine())
9494 mParent->onMachineDataChange(mData->mUuid);
9495 }
9496
9497 LogFlowThisFunc(("rc=%08X\n", rc));
9498 LogFlowThisFuncLeave();
9499 return rc;
9500}
9501
9502/**
9503 * Implementation for saving the machine settings into the given
9504 * settings::MachineConfigFile instance. This copies machine extradata
9505 * from the previous machine config file in the instance data, if any.
9506 *
9507 * This gets called from two locations:
9508 *
9509 * -- Machine::saveSettings(), during the regular XML writing;
9510 *
9511 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9512 * exported to OVF and we write the VirtualBox proprietary XML
9513 * into a <vbox:Machine> tag.
9514 *
9515 * This routine fills all the fields in there, including snapshots, *except*
9516 * for the following:
9517 *
9518 * -- fCurrentStateModified. There is some special logic associated with that.
9519 *
9520 * The caller can then call MachineConfigFile::write() or do something else
9521 * with it.
9522 *
9523 * Caller must hold the machine lock!
9524 *
9525 * This throws XML errors and HRESULT, so the caller must have a catch block!
9526 */
9527void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9528{
9529 // deep copy extradata
9530 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9531
9532 config.uuid = mData->mUuid;
9533
9534 // copy name, description, OS type, teleport, UTC etc.
9535 config.machineUserData = mUserData->s;
9536
9537 if ( mData->mMachineState == MachineState_Saved
9538 || mData->mMachineState == MachineState_Restoring
9539 // when deleting a snapshot we may or may not have a saved state in the current state,
9540 // so let's not assert here please
9541 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9542 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9543 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9544 && (!mSSData->strStateFilePath.isEmpty())
9545 )
9546 )
9547 {
9548 Assert(!mSSData->strStateFilePath.isEmpty());
9549 /* try to make the file name relative to the settings file dir */
9550 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9551 }
9552 else
9553 {
9554 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9555 config.strStateFile.setNull();
9556 }
9557
9558 if (mData->mCurrentSnapshot)
9559 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9560 else
9561 config.uuidCurrentSnapshot.clear();
9562
9563 config.timeLastStateChange = mData->mLastStateChange;
9564 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9565 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9566
9567 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9568 if (FAILED(rc)) throw rc;
9569
9570 rc = saveStorageControllers(config.storageMachine);
9571 if (FAILED(rc)) throw rc;
9572
9573 // save machine's media registry if this is VirtualBox 4.0 or later
9574 if (config.canHaveOwnMediaRegistry())
9575 {
9576 // determine machine folder
9577 Utf8Str strMachineFolder = getSettingsFileFull();
9578 strMachineFolder.stripFilename();
9579 mParent->saveMediaRegistry(config.mediaRegistry,
9580 getId(), // only media with registry ID == machine UUID
9581 strMachineFolder);
9582 // this throws HRESULT
9583 }
9584
9585 // save snapshots
9586 rc = saveAllSnapshots(config);
9587 if (FAILED(rc)) throw rc;
9588}
9589
9590/**
9591 * Saves all snapshots of the machine into the given machine config file. Called
9592 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9593 * @param config
9594 * @return
9595 */
9596HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9597{
9598 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9599
9600 HRESULT rc = S_OK;
9601
9602 try
9603 {
9604 config.llFirstSnapshot.clear();
9605
9606 if (mData->mFirstSnapshot)
9607 {
9608 settings::Snapshot snapNew;
9609 config.llFirstSnapshot.push_back(snapNew);
9610
9611 // get reference to the fresh copy of the snapshot on the list and
9612 // work on that copy directly to avoid excessive copying later
9613 settings::Snapshot &snap = config.llFirstSnapshot.front();
9614
9615 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9616 if (FAILED(rc)) throw rc;
9617 }
9618
9619// if (mType == IsSessionMachine)
9620// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9621
9622 }
9623 catch (HRESULT err)
9624 {
9625 /* we assume that error info is set by the thrower */
9626 rc = err;
9627 }
9628 catch (...)
9629 {
9630 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9631 }
9632
9633 return rc;
9634}
9635
9636/**
9637 * Saves the VM hardware configuration. It is assumed that the
9638 * given node is empty.
9639 *
9640 * @param data Reference to the settings object for the hardware config.
9641 * @param pDbg Pointer to the settings object for the debugging config
9642 * which happens to live in mHWData.
9643 * @param pAutostart Pointer to the settings object for the autostart config
9644 * which happens to live in mHWData.
9645 */
9646HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9647 settings::Autostart *pAutostart)
9648{
9649 HRESULT rc = S_OK;
9650
9651 try
9652 {
9653 /* The hardware version attribute (optional).
9654 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9655 if ( mHWData->mHWVersion == "1"
9656 && mSSData->strStateFilePath.isEmpty()
9657 )
9658 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
9659
9660 data.strVersion = mHWData->mHWVersion;
9661 data.uuid = mHWData->mHardwareUUID;
9662
9663 // CPU
9664 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9665 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9666 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9667 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9668 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9669 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9670 data.fPAE = !!mHWData->mPAEEnabled;
9671 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9672
9673 /* Standard and Extended CPUID leafs. */
9674 data.llCpuIdLeafs.clear();
9675 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9676 {
9677 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9678 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9679 }
9680 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9681 {
9682 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9683 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9684 }
9685
9686 data.cCPUs = mHWData->mCPUCount;
9687 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9688 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9689
9690 data.llCpus.clear();
9691 if (data.fCpuHotPlug)
9692 {
9693 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9694 {
9695 if (mHWData->mCPUAttached[idx])
9696 {
9697 settings::Cpu cpu;
9698 cpu.ulId = idx;
9699 data.llCpus.push_back(cpu);
9700 }
9701 }
9702 }
9703
9704 // memory
9705 data.ulMemorySizeMB = mHWData->mMemorySize;
9706 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9707
9708 // firmware
9709 data.firmwareType = mHWData->mFirmwareType;
9710
9711 // HID
9712 data.pointingHIDType = mHWData->mPointingHIDType;
9713 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9714
9715 // chipset
9716 data.chipsetType = mHWData->mChipsetType;
9717
9718 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9719
9720 // HPET
9721 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9722
9723 // boot order
9724 data.mapBootOrder.clear();
9725 for (size_t i = 0;
9726 i < RT_ELEMENTS(mHWData->mBootOrder);
9727 ++i)
9728 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9729
9730 // display
9731 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9732 data.cMonitors = mHWData->mMonitorCount;
9733 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9734 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9735 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9736 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9737 data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled;
9738 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9739
9740 /* VRDEServer settings (optional) */
9741 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9742 if (FAILED(rc)) throw rc;
9743
9744 /* BIOS (required) */
9745 rc = mBIOSSettings->saveSettings(data.biosSettings);
9746 if (FAILED(rc)) throw rc;
9747
9748 /* USB Controller (required) */
9749 rc = mUSBController->saveSettings(data.usbController);
9750 if (FAILED(rc)) throw rc;
9751
9752 /* Network adapters (required) */
9753 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9754 data.llNetworkAdapters.clear();
9755 /* Write out only the nominal number of network adapters for this
9756 * chipset type. Since Machine::commit() hasn't been called there
9757 * may be extra NIC settings in the vector. */
9758 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9759 {
9760 settings::NetworkAdapter nic;
9761 nic.ulSlot = slot;
9762 /* paranoia check... must not be NULL, but must not crash either. */
9763 if (mNetworkAdapters[slot])
9764 {
9765 rc = mNetworkAdapters[slot]->saveSettings(nic);
9766 if (FAILED(rc)) throw rc;
9767
9768 data.llNetworkAdapters.push_back(nic);
9769 }
9770 }
9771
9772 /* Serial ports */
9773 data.llSerialPorts.clear();
9774 for (ULONG slot = 0;
9775 slot < RT_ELEMENTS(mSerialPorts);
9776 ++slot)
9777 {
9778 settings::SerialPort s;
9779 s.ulSlot = slot;
9780 rc = mSerialPorts[slot]->saveSettings(s);
9781 if (FAILED(rc)) return rc;
9782
9783 data.llSerialPorts.push_back(s);
9784 }
9785
9786 /* Parallel ports */
9787 data.llParallelPorts.clear();
9788 for (ULONG slot = 0;
9789 slot < RT_ELEMENTS(mParallelPorts);
9790 ++slot)
9791 {
9792 settings::ParallelPort p;
9793 p.ulSlot = slot;
9794 rc = mParallelPorts[slot]->saveSettings(p);
9795 if (FAILED(rc)) return rc;
9796
9797 data.llParallelPorts.push_back(p);
9798 }
9799
9800 /* Audio adapter */
9801 rc = mAudioAdapter->saveSettings(data.audioAdapter);
9802 if (FAILED(rc)) return rc;
9803
9804 /* Shared folders */
9805 data.llSharedFolders.clear();
9806 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
9807 it != mHWData->mSharedFolders.end();
9808 ++it)
9809 {
9810 SharedFolder *pSF = *it;
9811 AutoCaller sfCaller(pSF);
9812 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
9813 settings::SharedFolder sf;
9814 sf.strName = pSF->getName();
9815 sf.strHostPath = pSF->getHostPath();
9816 sf.fWritable = !!pSF->isWritable();
9817 sf.fAutoMount = !!pSF->isAutoMounted();
9818
9819 data.llSharedFolders.push_back(sf);
9820 }
9821
9822 // clipboard
9823 data.clipboardMode = mHWData->mClipboardMode;
9824
9825 // drag'n'drop
9826 data.dragAndDropMode = mHWData->mDragAndDropMode;
9827
9828 /* Guest */
9829 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
9830
9831 // IO settings
9832 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
9833 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
9834
9835 /* BandwidthControl (required) */
9836 rc = mBandwidthControl->saveSettings(data.ioSettings);
9837 if (FAILED(rc)) throw rc;
9838
9839 /* Host PCI devices */
9840 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
9841 it != mHWData->mPCIDeviceAssignments.end();
9842 ++it)
9843 {
9844 ComObjPtr<PCIDeviceAttachment> pda = *it;
9845 settings::HostPCIDeviceAttachment hpda;
9846
9847 rc = pda->saveSettings(hpda);
9848 if (FAILED(rc)) throw rc;
9849
9850 data.pciAttachments.push_back(hpda);
9851 }
9852
9853
9854 // guest properties
9855 data.llGuestProperties.clear();
9856#ifdef VBOX_WITH_GUEST_PROPS
9857 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin();
9858 it != mHWData->mGuestProperties.end();
9859 ++it)
9860 {
9861 HWData::GuestProperty property = *it;
9862
9863 /* Remove transient guest properties at shutdown unless we
9864 * are saving state */
9865 if ( ( mData->mMachineState == MachineState_PoweredOff
9866 || mData->mMachineState == MachineState_Aborted
9867 || mData->mMachineState == MachineState_Teleported)
9868 && ( property.mFlags & guestProp::TRANSIENT
9869 || property.mFlags & guestProp::TRANSRESET))
9870 continue;
9871 settings::GuestProperty prop;
9872 prop.strName = property.strName;
9873 prop.strValue = property.strValue;
9874 prop.timestamp = property.mTimestamp;
9875 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
9876 guestProp::writeFlags(property.mFlags, szFlags);
9877 prop.strFlags = szFlags;
9878
9879 data.llGuestProperties.push_back(prop);
9880 }
9881
9882 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
9883 /* I presume this doesn't require a backup(). */
9884 mData->mGuestPropertiesModified = FALSE;
9885#endif /* VBOX_WITH_GUEST_PROPS defined */
9886
9887 *pDbg = mHWData->mDebugging;
9888 *pAutostart = mHWData->mAutostart;
9889 }
9890 catch(std::bad_alloc &)
9891 {
9892 return E_OUTOFMEMORY;
9893 }
9894
9895 AssertComRC(rc);
9896 return rc;
9897}
9898
9899/**
9900 * Saves the storage controller configuration.
9901 *
9902 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
9903 */
9904HRESULT Machine::saveStorageControllers(settings::Storage &data)
9905{
9906 data.llStorageControllers.clear();
9907
9908 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9909 it != mStorageControllers->end();
9910 ++it)
9911 {
9912 HRESULT rc;
9913 ComObjPtr<StorageController> pCtl = *it;
9914
9915 settings::StorageController ctl;
9916 ctl.strName = pCtl->getName();
9917 ctl.controllerType = pCtl->getControllerType();
9918 ctl.storageBus = pCtl->getStorageBus();
9919 ctl.ulInstance = pCtl->getInstance();
9920 ctl.fBootable = pCtl->getBootable();
9921
9922 /* Save the port count. */
9923 ULONG portCount;
9924 rc = pCtl->COMGETTER(PortCount)(&portCount);
9925 ComAssertComRCRet(rc, rc);
9926 ctl.ulPortCount = portCount;
9927
9928 /* Save fUseHostIOCache */
9929 BOOL fUseHostIOCache;
9930 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
9931 ComAssertComRCRet(rc, rc);
9932 ctl.fUseHostIOCache = !!fUseHostIOCache;
9933
9934 /* Save IDE emulation settings. */
9935 if (ctl.controllerType == StorageControllerType_IntelAhci)
9936 {
9937 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
9938 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
9939 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
9940 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
9941 )
9942 ComAssertComRCRet(rc, rc);
9943 }
9944
9945 /* save the devices now. */
9946 rc = saveStorageDevices(pCtl, ctl);
9947 ComAssertComRCRet(rc, rc);
9948
9949 data.llStorageControllers.push_back(ctl);
9950 }
9951
9952 return S_OK;
9953}
9954
9955/**
9956 * Saves the hard disk configuration.
9957 */
9958HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
9959 settings::StorageController &data)
9960{
9961 MediaData::AttachmentList atts;
9962
9963 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
9964 if (FAILED(rc)) return rc;
9965
9966 data.llAttachedDevices.clear();
9967 for (MediaData::AttachmentList::const_iterator it = atts.begin();
9968 it != atts.end();
9969 ++it)
9970 {
9971 settings::AttachedDevice dev;
9972
9973 MediumAttachment *pAttach = *it;
9974 Medium *pMedium = pAttach->getMedium();
9975
9976 dev.deviceType = pAttach->getType();
9977 dev.lPort = pAttach->getPort();
9978 dev.lDevice = pAttach->getDevice();
9979 if (pMedium)
9980 {
9981 if (pMedium->isHostDrive())
9982 dev.strHostDriveSrc = pMedium->getLocationFull();
9983 else
9984 dev.uuid = pMedium->getId();
9985 dev.fPassThrough = pAttach->getPassthrough();
9986 dev.fTempEject = pAttach->getTempEject();
9987 dev.fDiscard = pAttach->getDiscard();
9988 }
9989
9990 dev.strBwGroup = pAttach->getBandwidthGroup();
9991
9992 data.llAttachedDevices.push_back(dev);
9993 }
9994
9995 return S_OK;
9996}
9997
9998/**
9999 * Saves machine state settings as defined by aFlags
10000 * (SaveSTS_* values).
10001 *
10002 * @param aFlags Combination of SaveSTS_* flags.
10003 *
10004 * @note Locks objects for writing.
10005 */
10006HRESULT Machine::saveStateSettings(int aFlags)
10007{
10008 if (aFlags == 0)
10009 return S_OK;
10010
10011 AutoCaller autoCaller(this);
10012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10013
10014 /* This object's write lock is also necessary to serialize file access
10015 * (prevent concurrent reads and writes) */
10016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10017
10018 HRESULT rc = S_OK;
10019
10020 Assert(mData->pMachineConfigFile);
10021
10022 try
10023 {
10024 if (aFlags & SaveSTS_CurStateModified)
10025 mData->pMachineConfigFile->fCurrentStateModified = true;
10026
10027 if (aFlags & SaveSTS_StateFilePath)
10028 {
10029 if (!mSSData->strStateFilePath.isEmpty())
10030 /* try to make the file name relative to the settings file dir */
10031 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10032 else
10033 mData->pMachineConfigFile->strStateFile.setNull();
10034 }
10035
10036 if (aFlags & SaveSTS_StateTimeStamp)
10037 {
10038 Assert( mData->mMachineState != MachineState_Aborted
10039 || mSSData->strStateFilePath.isEmpty());
10040
10041 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10042
10043 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10044//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10045 }
10046
10047 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10048 }
10049 catch (...)
10050 {
10051 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10052 }
10053
10054 return rc;
10055}
10056
10057/**
10058 * Ensures that the given medium is added to a media registry. If this machine
10059 * was created with 4.0 or later, then the machine registry is used. Otherwise
10060 * the global VirtualBox media registry is used.
10061 *
10062 * Caller must NOT hold machine lock, media tree or any medium locks!
10063 *
10064 * @param pMedium
10065 */
10066void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10067{
10068 /* Paranoia checks: do not hold machine or media tree locks. */
10069 AssertReturnVoid(!isWriteLockOnCurrentThread());
10070 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10071
10072 ComObjPtr<Medium> pBase;
10073 {
10074 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10075 pBase = pMedium->getBase();
10076 }
10077
10078 /* Paranoia checks: do not hold medium locks. */
10079 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10080 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10081
10082 // decide which medium registry to use now that the medium is attached:
10083 Guid uuid;
10084 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10085 // machine XML is VirtualBox 4.0 or higher:
10086 uuid = getId(); // machine UUID
10087 else
10088 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10089
10090 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10091 mParent->markRegistryModified(uuid);
10092
10093 /* For more complex hard disk structures it can happen that the base
10094 * medium isn't yet associated with any medium registry. Do that now. */
10095 if (pMedium != pBase)
10096 {
10097 if (pBase->addRegistry(uuid, true /* fRecurse */))
10098 mParent->markRegistryModified(uuid);
10099 }
10100}
10101
10102/**
10103 * Creates differencing hard disks for all normal hard disks attached to this
10104 * machine and a new set of attachments to refer to created disks.
10105 *
10106 * Used when taking a snapshot or when deleting the current state. Gets called
10107 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10108 *
10109 * This method assumes that mMediaData contains the original hard disk attachments
10110 * it needs to create diffs for. On success, these attachments will be replaced
10111 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10112 * called to delete created diffs which will also rollback mMediaData and restore
10113 * whatever was backed up before calling this method.
10114 *
10115 * Attachments with non-normal hard disks are left as is.
10116 *
10117 * If @a aOnline is @c false then the original hard disks that require implicit
10118 * diffs will be locked for reading. Otherwise it is assumed that they are
10119 * already locked for writing (when the VM was started). Note that in the latter
10120 * case it is responsibility of the caller to lock the newly created diffs for
10121 * writing if this method succeeds.
10122 *
10123 * @param aProgress Progress object to run (must contain at least as
10124 * many operations left as the number of hard disks
10125 * attached).
10126 * @param aOnline Whether the VM was online prior to this operation.
10127 *
10128 * @note The progress object is not marked as completed, neither on success nor
10129 * on failure. This is a responsibility of the caller.
10130 *
10131 * @note Locks this object for writing.
10132 */
10133HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10134 ULONG aWeight,
10135 bool aOnline)
10136{
10137 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10138
10139 AutoCaller autoCaller(this);
10140 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10141
10142 AutoMultiWriteLock2 alock(this->lockHandle(),
10143 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10144
10145 /* must be in a protective state because we release the lock below */
10146 AssertReturn( mData->mMachineState == MachineState_Saving
10147 || mData->mMachineState == MachineState_LiveSnapshotting
10148 || mData->mMachineState == MachineState_RestoringSnapshot
10149 || mData->mMachineState == MachineState_DeletingSnapshot
10150 , E_FAIL);
10151
10152 HRESULT rc = S_OK;
10153
10154 MediumLockListMap lockedMediaOffline;
10155 MediumLockListMap *lockedMediaMap;
10156 if (aOnline)
10157 lockedMediaMap = &mData->mSession.mLockedMedia;
10158 else
10159 lockedMediaMap = &lockedMediaOffline;
10160
10161 try
10162 {
10163 if (!aOnline)
10164 {
10165 /* lock all attached hard disks early to detect "in use"
10166 * situations before creating actual diffs */
10167 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10168 it != mMediaData->mAttachments.end();
10169 ++it)
10170 {
10171 MediumAttachment* pAtt = *it;
10172 if (pAtt->getType() == DeviceType_HardDisk)
10173 {
10174 Medium* pMedium = pAtt->getMedium();
10175 Assert(pMedium);
10176
10177 MediumLockList *pMediumLockList(new MediumLockList());
10178 alock.release();
10179 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10180 false /* fMediumLockWrite */,
10181 NULL,
10182 *pMediumLockList);
10183 alock.acquire();
10184 if (FAILED(rc))
10185 {
10186 delete pMediumLockList;
10187 throw rc;
10188 }
10189 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10190 if (FAILED(rc))
10191 {
10192 throw setError(rc,
10193 tr("Collecting locking information for all attached media failed"));
10194 }
10195 }
10196 }
10197
10198 /* Now lock all media. If this fails, nothing is locked. */
10199 alock.release();
10200 rc = lockedMediaMap->Lock();
10201 alock.acquire();
10202 if (FAILED(rc))
10203 {
10204 throw setError(rc,
10205 tr("Locking of attached media failed"));
10206 }
10207 }
10208
10209 /* remember the current list (note that we don't use backup() since
10210 * mMediaData may be already backed up) */
10211 MediaData::AttachmentList atts = mMediaData->mAttachments;
10212
10213 /* start from scratch */
10214 mMediaData->mAttachments.clear();
10215
10216 /* go through remembered attachments and create diffs for normal hard
10217 * disks and attach them */
10218 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10219 it != atts.end();
10220 ++it)
10221 {
10222 MediumAttachment* pAtt = *it;
10223
10224 DeviceType_T devType = pAtt->getType();
10225 Medium* pMedium = pAtt->getMedium();
10226
10227 if ( devType != DeviceType_HardDisk
10228 || pMedium == NULL
10229 || pMedium->getType() != MediumType_Normal)
10230 {
10231 /* copy the attachment as is */
10232
10233 /** @todo the progress object created in Console::TakeSnaphot
10234 * only expects operations for hard disks. Later other
10235 * device types need to show up in the progress as well. */
10236 if (devType == DeviceType_HardDisk)
10237 {
10238 if (pMedium == NULL)
10239 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10240 aWeight); // weight
10241 else
10242 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10243 pMedium->getBase()->getName().c_str()).raw(),
10244 aWeight); // weight
10245 }
10246
10247 mMediaData->mAttachments.push_back(pAtt);
10248 continue;
10249 }
10250
10251 /* need a diff */
10252 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10253 pMedium->getBase()->getName().c_str()).raw(),
10254 aWeight); // weight
10255
10256 Utf8Str strFullSnapshotFolder;
10257 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10258
10259 ComObjPtr<Medium> diff;
10260 diff.createObject();
10261 // store the diff in the same registry as the parent
10262 // (this cannot fail here because we can't create implicit diffs for
10263 // unregistered images)
10264 Guid uuidRegistryParent;
10265 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10266 Assert(fInRegistry); NOREF(fInRegistry);
10267 rc = diff->init(mParent,
10268 pMedium->getPreferredDiffFormat(),
10269 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10270 uuidRegistryParent);
10271 if (FAILED(rc)) throw rc;
10272
10273 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10274 * the push_back? Looks like we're going to release medium with the
10275 * wrong kind of lock (general issue with if we fail anywhere at all)
10276 * and an orphaned VDI in the snapshots folder. */
10277
10278 /* update the appropriate lock list */
10279 MediumLockList *pMediumLockList;
10280 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10281 AssertComRCThrowRC(rc);
10282 if (aOnline)
10283 {
10284 alock.release();
10285 rc = pMediumLockList->Update(pMedium, false);
10286 alock.acquire();
10287 AssertComRCThrowRC(rc);
10288 }
10289 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10290 AssertComRCThrowRC(rc);
10291
10292 /* release the locks before the potentially lengthy operation */
10293 alock.release();
10294 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10295 pMediumLockList,
10296 NULL /* aProgress */,
10297 true /* aWait */);
10298 alock.acquire();
10299 if (FAILED(rc)) throw rc;
10300
10301 rc = lockedMediaMap->Unlock();
10302 AssertComRCThrowRC(rc);
10303 alock.release();
10304 rc = pMediumLockList->Append(diff, true);
10305 alock.acquire();
10306 AssertComRCThrowRC(rc);
10307 alock.release();
10308 rc = lockedMediaMap->Lock();
10309 alock.acquire();
10310 AssertComRCThrowRC(rc);
10311
10312 rc = diff->addBackReference(mData->mUuid);
10313 AssertComRCThrowRC(rc);
10314
10315 /* add a new attachment */
10316 ComObjPtr<MediumAttachment> attachment;
10317 attachment.createObject();
10318 rc = attachment->init(this,
10319 diff,
10320 pAtt->getControllerName(),
10321 pAtt->getPort(),
10322 pAtt->getDevice(),
10323 DeviceType_HardDisk,
10324 true /* aImplicit */,
10325 false /* aPassthrough */,
10326 false /* aTempEject */,
10327 pAtt->getNonRotational(),
10328 pAtt->getDiscard(),
10329 pAtt->getBandwidthGroup());
10330 if (FAILED(rc)) throw rc;
10331
10332 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10333 AssertComRCThrowRC(rc);
10334 mMediaData->mAttachments.push_back(attachment);
10335 }
10336 }
10337 catch (HRESULT aRC) { rc = aRC; }
10338
10339
10340 /* unlock all hard disks we locked */
10341 if (!aOnline)
10342 {
10343 ErrorInfoKeeper eik;
10344
10345 HRESULT rc1 = lockedMediaMap->Clear();
10346 AssertComRC(rc1);
10347 }
10348
10349
10350 if (FAILED(rc))
10351 {
10352 MultiResult mrc = rc;
10353
10354 alock.release();
10355 mrc = deleteImplicitDiffs(aOnline);
10356 }
10357
10358 if (aOnline)
10359 mData->mSession.mLockedMedia = *lockedMediaMap;
10360 else
10361 lockedMediaOffline = *lockedMediaMap;
10362
10363 return rc;
10364}
10365
10366/**
10367 * Deletes implicit differencing hard disks created either by
10368 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10369 *
10370 * Note that to delete hard disks created by #AttachDevice() this method is
10371 * called from #fixupMedia() when the changes are rolled back.
10372 *
10373 * @note Locks this object for writing.
10374 */
10375// Tod - deal withh offline atts .....
10376HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10377{
10378 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10379
10380 AutoCaller autoCaller(this);
10381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10382
10383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10384 AutoMultiWriteLock2 alock2(this->lockHandle(),
10385 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10386
10387 /* Don't want backed up state. */
10388 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10389
10390 HRESULT rc = S_OK;
10391 MachineState_T oldState = mData->mMachineState;
10392
10393 MediaData::AttachmentList implicitAtts;
10394 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10395
10396 // Locked Media Map.
10397 MediumLockListMap lockedMediaOffline;
10398 MediumLockListMap *lockedMediaMap;
10399 if (aOnline)
10400 lockedMediaMap = &mData->mSession.mLockedMedia;
10401 else
10402 lockedMediaMap = &lockedMediaOffline;
10403
10404 try
10405 {
10406 // create Medium lock lists for offline atts as per createImplicitAtts.
10407 if (!aOnline)
10408 {
10409 /* lock all attached hard disks early to detect "in use"
10410 * situations before deleting actual diffs */
10411 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10412 it != mMediaData->mAttachments.end();
10413 ++it)
10414 {
10415 MediumAttachment* pAtt = *it;
10416 if (pAtt->getType() == DeviceType_HardDisk)
10417 {
10418 Medium* pMedium = pAtt->getMedium();
10419 Assert(pMedium);
10420
10421 MediumLockList *pMediumLockList(new MediumLockList());
10422 alock.release();
10423 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10424 false /* fMediumLockWrite */,
10425 NULL,
10426 *pMediumLockList);
10427 alock.acquire();
10428
10429 if (FAILED(rc))
10430 {
10431 delete pMediumLockList;
10432 throw rc;
10433 }
10434
10435 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10436 if (FAILED(rc))
10437 throw rc;
10438 }
10439 }
10440
10441 if (FAILED(rc))
10442 throw(rc);
10443
10444 } // end of offline
10445
10446
10447 /* Start from scratch */
10448
10449 /* Go through remembered attachments and create diffs for normal hard
10450 * disks and attach them */
10451 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10452 it != mMediaData->mAttachments.end();
10453 ++it)
10454 {
10455 MediumAttachment* pAtt = *it;
10456
10457 // Ensure we get a hard disk.
10458 ComObjPtr<Medium> hd = pAtt->getMedium();
10459
10460 if (hd.isNull())
10461 continue;
10462
10463 // DeviceType_T devType = pAtt->getType();
10464
10465 // Implicit atts go on the list of implicit atts for deletion and back reference gets removed.
10466 if (pAtt->isImplicit())
10467 {
10468 /* Deassociate and mark for deletion */
10469 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10470 rc = hd->removeBackReference(mData->mUuid);
10471 if (FAILED(rc))
10472 throw(rc);
10473 implicitAtts.push_back(pAtt);
10474 continue;
10475 }
10476
10477 /* Was this hard disk attached before? */
10478 if (!findAttachment(oldAtts, hd))
10479 {
10480 /* no: de-associate */
10481 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10482 rc = hd->removeBackReference(mData->mUuid);
10483 if (FAILED(rc))
10484 throw setError(rc);
10485 continue;
10486 }
10487 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10488 }
10489
10490 /* rollback hard disk changes */
10491 mMediaData.rollback();
10492
10493 MultiResult mrc(S_OK);
10494
10495 // Delete unused implicit diffs.
10496 if(implicitAtts.size() != 0)
10497 {
10498 if ( oldState != MachineState_Saving
10499 && oldState != MachineState_LiveSnapshotting
10500 && oldState != MachineState_RestoringSnapshot
10501 && oldState != MachineState_DeletingSnapshot
10502 && oldState != MachineState_DeletingSnapshotOnline
10503 && oldState != MachineState_DeletingSnapshotPaused
10504 )
10505 setMachineState(MachineState_SettingUp);
10506
10507 alock.release();
10508
10509 // Loop round implicitatts
10510 // a) Remove medum lock list.
10511 // b) Delete HD storage from media list.
10512 // c) Remove medium lock list.
10513 MediaData::AttachmentList::const_iterator ittodelete;
10514 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10515 it != implicitAtts.end();
10516 ++it)
10517 {
10518 // Remove attachment.
10519 MediumAttachment* pAtt = *it;
10520 Assert(pAtt);
10521 LogFlowThisFunc(("Deleting '%s'\n", (pAtt)->getLogName()));
10522 ComObjPtr<Medium> hd = pAtt->getMedium();
10523 Assert(hd);
10524 mMediaData->mAttachments.remove(pAtt);
10525 rc = lockedMediaMap->Unlock();
10526
10527 if (FAILED(rc))
10528 throw(rc);
10529
10530 // Remove from locked media map.
10531 MediumLockList *pMediumLockList;
10532 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10533
10534 if (FAILED(rc))
10535 throw(rc);
10536 rc = lockedMediaMap->Remove(pAtt);
10537 if (FAILED(rc))
10538 throw(rc);
10539 rc = lockedMediaMap->Lock();
10540 alock2.release();
10541 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10542
10543 if (FAILED(rc))
10544 throw(rc);
10545 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), hd->getLocationFull().c_str() ));
10546
10547 // Only way to delete lock list entry is
10548 // by iterator so find the iterator with this lock list entry.
10549 // Remove from Media Lock List.
10550 MediumLockList::Base::iterator lockListBegin = pMediumLockList->GetBegin();
10551 MediumLockList::Base::iterator lockListEnd = pMediumLockList->GetEnd();
10552 for (MediumLockList::Base::iterator it2 = lockListBegin; it2 != lockListEnd; ++it2 )
10553 {
10554 MediumLock &mediumLock = *it2;
10555 const ComObjPtr<Medium> pMedium = mediumLock.GetMedium();
10556
10557 if (pMedium == hd)
10558 {
10559 rc = pMediumLockList->RemoveByIterator(it2);
10560 if (FAILED(rc))
10561 throw(rc);
10562 break;
10563 }
10564 }
10565 }
10566 }
10567 if (mData->mMachineState == MachineState_SettingUp)
10568 setMachineState(oldState);
10569 }
10570
10571 catch (HRESULT aRC) {rc = aRC;}
10572
10573 // Unlock all hard disks that we locked if offline).
10574 if (!aOnline)
10575 {
10576 ErrorInfoKeeper eik;
10577 rc = lockedMediaMap->Clear();
10578 if (FAILED(rc))
10579 throw (rc);
10580 }
10581
10582
10583 if (aOnline)
10584 mData->mSession.mLockedMedia = *lockedMediaMap;
10585 else
10586 lockedMediaOffline = *lockedMediaMap;
10587
10588 return rc;
10589}
10590
10591
10592/**
10593 * Looks through the given list of media attachments for one with the given parameters
10594 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10595 * can be searched as well if needed.
10596 *
10597 * @param list
10598 * @param aControllerName
10599 * @param aControllerPort
10600 * @param aDevice
10601 * @return
10602 */
10603MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10604 IN_BSTR aControllerName,
10605 LONG aControllerPort,
10606 LONG aDevice)
10607{
10608 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10609 it != ll.end();
10610 ++it)
10611 {
10612 MediumAttachment *pAttach = *it;
10613 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10614 return pAttach;
10615 }
10616
10617 return NULL;
10618}
10619
10620/**
10621 * Looks through the given list of media attachments for one with the given parameters
10622 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10623 * can be searched as well if needed.
10624 *
10625 * @param list
10626 * @param aControllerName
10627 * @param aControllerPort
10628 * @param aDevice
10629 * @return
10630 */
10631MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10632 ComObjPtr<Medium> pMedium)
10633{
10634 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10635 it != ll.end();
10636 ++it)
10637 {
10638 MediumAttachment *pAttach = *it;
10639 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10640 if (pMediumThis == pMedium)
10641 return pAttach;
10642 }
10643
10644 return NULL;
10645}
10646
10647/**
10648 * Looks through the given list of media attachments for one with the given parameters
10649 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10650 * can be searched as well if needed.
10651 *
10652 * @param list
10653 * @param aControllerName
10654 * @param aControllerPort
10655 * @param aDevice
10656 * @return
10657 */
10658MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10659 Guid &id)
10660{
10661 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10662 it != ll.end();
10663 ++it)
10664 {
10665 MediumAttachment *pAttach = *it;
10666 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10667 if (pMediumThis->getId() == id)
10668 return pAttach;
10669 }
10670
10671 return NULL;
10672}
10673
10674/**
10675 * Main implementation for Machine::DetachDevice. This also gets called
10676 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10677 *
10678 * @param pAttach Medium attachment to detach.
10679 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10680 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10681 * @return
10682 */
10683HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10684 AutoWriteLock &writeLock,
10685 Snapshot *pSnapshot)
10686{
10687 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10688 DeviceType_T mediumType = pAttach->getType();
10689
10690 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10691
10692 if (pAttach->isImplicit())
10693 {
10694 /* attempt to implicitly delete the implicitly created diff */
10695
10696 /// @todo move the implicit flag from MediumAttachment to Medium
10697 /// and forbid any hard disk operation when it is implicit. Or maybe
10698 /// a special media state for it to make it even more simple.
10699
10700 Assert(mMediaData.isBackedUp());
10701
10702 /* will release the lock before the potentially lengthy operation, so
10703 * protect with the special state */
10704 MachineState_T oldState = mData->mMachineState;
10705 setMachineState(MachineState_SettingUp);
10706
10707 writeLock.release();
10708
10709 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10710 true /*aWait*/);
10711
10712 writeLock.acquire();
10713
10714 setMachineState(oldState);
10715
10716 if (FAILED(rc)) return rc;
10717 }
10718
10719 setModified(IsModified_Storage);
10720 mMediaData.backup();
10721 mMediaData->mAttachments.remove(pAttach);
10722
10723 if (!oldmedium.isNull())
10724 {
10725 // if this is from a snapshot, do not defer detachment to commitMedia()
10726 if (pSnapshot)
10727 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10728 // else if non-hard disk media, do not defer detachment to commitMedia() either
10729 else if (mediumType != DeviceType_HardDisk)
10730 oldmedium->removeBackReference(mData->mUuid);
10731 }
10732
10733 return S_OK;
10734}
10735
10736/**
10737 * Goes thru all media of the given list and
10738 *
10739 * 1) calls detachDevice() on each of them for this machine and
10740 * 2) adds all Medium objects found in the process to the given list,
10741 * depending on cleanupMode.
10742 *
10743 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10744 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10745 * media to the list.
10746 *
10747 * This gets called from Machine::Unregister, both for the actual Machine and
10748 * the SnapshotMachine objects that might be found in the snapshots.
10749 *
10750 * Requires caller and locking. The machine lock must be passed in because it
10751 * will be passed on to detachDevice which needs it for temporary unlocking.
10752 *
10753 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10754 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10755 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10756 * otherwise no media get added.
10757 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10758 * @return
10759 */
10760HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10761 Snapshot *pSnapshot,
10762 CleanupMode_T cleanupMode,
10763 MediaList &llMedia)
10764{
10765 Assert(isWriteLockOnCurrentThread());
10766
10767 HRESULT rc;
10768
10769 // make a temporary list because detachDevice invalidates iterators into
10770 // mMediaData->mAttachments
10771 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10772
10773 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10774 it != llAttachments2.end();
10775 ++it)
10776 {
10777 ComObjPtr<MediumAttachment> &pAttach = *it;
10778 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10779
10780 if (!pMedium.isNull())
10781 {
10782 AutoCaller mac(pMedium);
10783 if (FAILED(mac.rc())) return mac.rc();
10784 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10785 DeviceType_T devType = pMedium->getDeviceType();
10786 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10787 && devType == DeviceType_HardDisk)
10788 || (cleanupMode == CleanupMode_Full)
10789 )
10790 {
10791 llMedia.push_back(pMedium);
10792 ComObjPtr<Medium> pParent = pMedium->getParent();
10793 /*
10794 * Search for medias which are not attached to any machine, but
10795 * in the chain to an attached disk. Mediums are only consided
10796 * if they are:
10797 * - have only one child
10798 * - no references to any machines
10799 * - are of normal medium type
10800 */
10801 while (!pParent.isNull())
10802 {
10803 AutoCaller mac1(pParent);
10804 if (FAILED(mac1.rc())) return mac1.rc();
10805 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
10806 if (pParent->getChildren().size() == 1)
10807 {
10808 if ( pParent->getMachineBackRefCount() == 0
10809 && pParent->getType() == MediumType_Normal
10810 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
10811 llMedia.push_back(pParent);
10812 }
10813 else
10814 break;
10815 pParent = pParent->getParent();
10816 }
10817 }
10818 }
10819
10820 // real machine: then we need to use the proper method
10821 rc = detachDevice(pAttach, writeLock, pSnapshot);
10822
10823 if (FAILED(rc))
10824 return rc;
10825 }
10826
10827 return S_OK;
10828}
10829
10830/**
10831 * Perform deferred hard disk detachments.
10832 *
10833 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
10834 * backed up).
10835 *
10836 * If @a aOnline is @c true then this method will also unlock the old hard disks
10837 * for which the new implicit diffs were created and will lock these new diffs for
10838 * writing.
10839 *
10840 * @param aOnline Whether the VM was online prior to this operation.
10841 *
10842 * @note Locks this object for writing!
10843 */
10844void Machine::commitMedia(bool aOnline /*= false*/)
10845{
10846 AutoCaller autoCaller(this);
10847 AssertComRCReturnVoid(autoCaller.rc());
10848
10849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10850
10851 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
10852
10853 HRESULT rc = S_OK;
10854
10855 /* no attach/detach operations -- nothing to do */
10856 if (!mMediaData.isBackedUp())
10857 return;
10858
10859 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10860 bool fMediaNeedsLocking = false;
10861
10862 /* enumerate new attachments */
10863 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10864 it != mMediaData->mAttachments.end();
10865 ++it)
10866 {
10867 MediumAttachment *pAttach = *it;
10868
10869 pAttach->commit();
10870
10871 Medium* pMedium = pAttach->getMedium();
10872 bool fImplicit = pAttach->isImplicit();
10873
10874 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
10875 (pMedium) ? pMedium->getName().c_str() : "NULL",
10876 fImplicit));
10877
10878 /** @todo convert all this Machine-based voodoo to MediumAttachment
10879 * based commit logic. */
10880 if (fImplicit)
10881 {
10882 /* convert implicit attachment to normal */
10883 pAttach->setImplicit(false);
10884
10885 if ( aOnline
10886 && pMedium
10887 && pAttach->getType() == DeviceType_HardDisk
10888 )
10889 {
10890 ComObjPtr<Medium> parent = pMedium->getParent();
10891 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
10892
10893 /* update the appropriate lock list */
10894 MediumLockList *pMediumLockList;
10895 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10896 AssertComRC(rc);
10897 if (pMediumLockList)
10898 {
10899 /* unlock if there's a need to change the locking */
10900 if (!fMediaNeedsLocking)
10901 {
10902 rc = mData->mSession.mLockedMedia.Unlock();
10903 AssertComRC(rc);
10904 fMediaNeedsLocking = true;
10905 }
10906 rc = pMediumLockList->Update(parent, false);
10907 AssertComRC(rc);
10908 rc = pMediumLockList->Append(pMedium, true);
10909 AssertComRC(rc);
10910 }
10911 }
10912
10913 continue;
10914 }
10915
10916 if (pMedium)
10917 {
10918 /* was this medium attached before? */
10919 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
10920 oldIt != oldAtts.end();
10921 ++oldIt)
10922 {
10923 MediumAttachment *pOldAttach = *oldIt;
10924 if (pOldAttach->getMedium() == pMedium)
10925 {
10926 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
10927
10928 /* yes: remove from old to avoid de-association */
10929 oldAtts.erase(oldIt);
10930 break;
10931 }
10932 }
10933 }
10934 }
10935
10936 /* enumerate remaining old attachments and de-associate from the
10937 * current machine state */
10938 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
10939 it != oldAtts.end();
10940 ++it)
10941 {
10942 MediumAttachment *pAttach = *it;
10943 Medium* pMedium = pAttach->getMedium();
10944
10945 /* Detach only hard disks, since DVD/floppy media is detached
10946 * instantly in MountMedium. */
10947 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
10948 {
10949 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
10950
10951 /* now de-associate from the current machine state */
10952 rc = pMedium->removeBackReference(mData->mUuid);
10953 AssertComRC(rc);
10954
10955 if (aOnline)
10956 {
10957 /* unlock since medium is not used anymore */
10958 MediumLockList *pMediumLockList;
10959 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
10960 AssertComRC(rc);
10961 if (pMediumLockList)
10962 {
10963 rc = mData->mSession.mLockedMedia.Remove(pAttach);
10964 AssertComRC(rc);
10965 }
10966 }
10967 }
10968 }
10969
10970 /* take media locks again so that the locking state is consistent */
10971 if (fMediaNeedsLocking)
10972 {
10973 Assert(aOnline);
10974 rc = mData->mSession.mLockedMedia.Lock();
10975 AssertComRC(rc);
10976 }
10977
10978 /* commit the hard disk changes */
10979 mMediaData.commit();
10980
10981 if (isSessionMachine())
10982 {
10983 /*
10984 * Update the parent machine to point to the new owner.
10985 * This is necessary because the stored parent will point to the
10986 * session machine otherwise and cause crashes or errors later
10987 * when the session machine gets invalid.
10988 */
10989 /** @todo Change the MediumAttachment class to behave like any other
10990 * class in this regard by creating peer MediumAttachment
10991 * objects for session machines and share the data with the peer
10992 * machine.
10993 */
10994 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10995 it != mMediaData->mAttachments.end();
10996 ++it)
10997 {
10998 (*it)->updateParentMachine(mPeer);
10999 }
11000
11001 /* attach new data to the primary machine and reshare it */
11002 mPeer->mMediaData.attach(mMediaData);
11003 }
11004
11005 return;
11006}
11007
11008/**
11009 * Perform deferred deletion of implicitly created diffs.
11010 *
11011 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11012 * backed up).
11013 *
11014 * @note Locks this object for writing!
11015 */
11016void Machine::rollbackMedia()
11017{
11018 AutoCaller autoCaller(this);
11019 AssertComRCReturnVoid (autoCaller.rc());
11020
11021 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11022 LogFlowThisFunc(("Entering rollbackmedia\n"));
11023
11024 HRESULT rc = S_OK;
11025
11026 /* no attach/detach operations -- nothing to do */
11027 if (!mMediaData.isBackedUp())
11028 return;
11029
11030 /* enumerate new attachments */
11031 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11032 it != mMediaData->mAttachments.end();
11033 ++it)
11034 {
11035 MediumAttachment *pAttach = *it;
11036 /* Fix up the backrefs for DVD/floppy media. */
11037 if (pAttach->getType() != DeviceType_HardDisk)
11038 {
11039 Medium* pMedium = pAttach->getMedium();
11040 if (pMedium)
11041 {
11042 rc = pMedium->removeBackReference(mData->mUuid);
11043 AssertComRC(rc);
11044 }
11045 }
11046
11047 (*it)->rollback();
11048
11049 pAttach = *it;
11050 /* Fix up the backrefs for DVD/floppy media. */
11051 if (pAttach->getType() != DeviceType_HardDisk)
11052 {
11053 Medium* pMedium = pAttach->getMedium();
11054 if (pMedium)
11055 {
11056 rc = pMedium->addBackReference(mData->mUuid);
11057 AssertComRC(rc);
11058 }
11059 }
11060 }
11061
11062 /** @todo convert all this Machine-based voodoo to MediumAttachment
11063 * based rollback logic. */
11064 deleteImplicitDiffs(true);
11065
11066 return;
11067}
11068
11069/**
11070 * Returns true if the settings file is located in the directory named exactly
11071 * as the machine; this means, among other things, that the machine directory
11072 * should be auto-renamed.
11073 *
11074 * @param aSettingsDir if not NULL, the full machine settings file directory
11075 * name will be assigned there.
11076 *
11077 * @note Doesn't lock anything.
11078 * @note Not thread safe (must be called from this object's lock).
11079 */
11080bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11081{
11082 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11083 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11084 if (aSettingsDir)
11085 *aSettingsDir = strMachineDirName;
11086 strMachineDirName.stripPath(); // vmname
11087 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11088 strConfigFileOnly.stripPath() // vmname.vbox
11089 .stripExt(); // vmname
11090 /** @todo hack, make somehow use of ComposeMachineFilename */
11091 if (mUserData->s.fDirectoryIncludesUUID)
11092 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11093
11094 AssertReturn(!strMachineDirName.isEmpty(), false);
11095 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11096
11097 return strMachineDirName == strConfigFileOnly;
11098}
11099
11100/**
11101 * Discards all changes to machine settings.
11102 *
11103 * @param aNotify Whether to notify the direct session about changes or not.
11104 *
11105 * @note Locks objects for writing!
11106 */
11107void Machine::rollback(bool aNotify)
11108{
11109 AutoCaller autoCaller(this);
11110 AssertComRCReturn(autoCaller.rc(), (void)0);
11111
11112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11113
11114 if (!mStorageControllers.isNull())
11115 {
11116 if (mStorageControllers.isBackedUp())
11117 {
11118 /* unitialize all new devices (absent in the backed up list). */
11119 StorageControllerList::const_iterator it = mStorageControllers->begin();
11120 StorageControllerList *backedList = mStorageControllers.backedUpData();
11121 while (it != mStorageControllers->end())
11122 {
11123 if ( std::find(backedList->begin(), backedList->end(), *it)
11124 == backedList->end()
11125 )
11126 {
11127 (*it)->uninit();
11128 }
11129 ++it;
11130 }
11131
11132 /* restore the list */
11133 mStorageControllers.rollback();
11134 }
11135
11136 /* rollback any changes to devices after restoring the list */
11137 if (mData->flModifications & IsModified_Storage)
11138 {
11139 StorageControllerList::const_iterator it = mStorageControllers->begin();
11140 while (it != mStorageControllers->end())
11141 {
11142 (*it)->rollback();
11143 ++it;
11144 }
11145 }
11146 }
11147
11148 mUserData.rollback();
11149
11150 mHWData.rollback();
11151
11152 if (mData->flModifications & IsModified_Storage)
11153 rollbackMedia();
11154
11155 if (mBIOSSettings)
11156 mBIOSSettings->rollback();
11157
11158 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11159 mVRDEServer->rollback();
11160
11161 if (mAudioAdapter)
11162 mAudioAdapter->rollback();
11163
11164 if (mUSBController && (mData->flModifications & IsModified_USB))
11165 mUSBController->rollback();
11166
11167 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11168 mBandwidthControl->rollback();
11169
11170 if (!mHWData.isNull())
11171 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11172 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11173 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11174 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11175
11176 if (mData->flModifications & IsModified_NetworkAdapters)
11177 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11178 if ( mNetworkAdapters[slot]
11179 && mNetworkAdapters[slot]->isModified())
11180 {
11181 mNetworkAdapters[slot]->rollback();
11182 networkAdapters[slot] = mNetworkAdapters[slot];
11183 }
11184
11185 if (mData->flModifications & IsModified_SerialPorts)
11186 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11187 if ( mSerialPorts[slot]
11188 && mSerialPorts[slot]->isModified())
11189 {
11190 mSerialPorts[slot]->rollback();
11191 serialPorts[slot] = mSerialPorts[slot];
11192 }
11193
11194 if (mData->flModifications & IsModified_ParallelPorts)
11195 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11196 if ( mParallelPorts[slot]
11197 && mParallelPorts[slot]->isModified())
11198 {
11199 mParallelPorts[slot]->rollback();
11200 parallelPorts[slot] = mParallelPorts[slot];
11201 }
11202
11203 if (aNotify)
11204 {
11205 /* inform the direct session about changes */
11206
11207 ComObjPtr<Machine> that = this;
11208 uint32_t flModifications = mData->flModifications;
11209 alock.release();
11210
11211 if (flModifications & IsModified_SharedFolders)
11212 that->onSharedFolderChange();
11213
11214 if (flModifications & IsModified_VRDEServer)
11215 that->onVRDEServerChange(/* aRestart */ TRUE);
11216 if (flModifications & IsModified_USB)
11217 that->onUSBControllerChange();
11218
11219 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11220 if (networkAdapters[slot])
11221 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11222 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11223 if (serialPorts[slot])
11224 that->onSerialPortChange(serialPorts[slot]);
11225 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11226 if (parallelPorts[slot])
11227 that->onParallelPortChange(parallelPorts[slot]);
11228
11229 if (flModifications & IsModified_Storage)
11230 that->onStorageControllerChange();
11231
11232#if 0
11233 if (flModifications & IsModified_BandwidthControl)
11234 that->onBandwidthControlChange();
11235#endif
11236 }
11237}
11238
11239/**
11240 * Commits all the changes to machine settings.
11241 *
11242 * Note that this operation is supposed to never fail.
11243 *
11244 * @note Locks this object and children for writing.
11245 */
11246void Machine::commit()
11247{
11248 AutoCaller autoCaller(this);
11249 AssertComRCReturnVoid(autoCaller.rc());
11250
11251 AutoCaller peerCaller(mPeer);
11252 AssertComRCReturnVoid(peerCaller.rc());
11253
11254 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11255
11256 /*
11257 * use safe commit to ensure Snapshot machines (that share mUserData)
11258 * will still refer to a valid memory location
11259 */
11260 mUserData.commitCopy();
11261
11262 mHWData.commit();
11263
11264 if (mMediaData.isBackedUp())
11265 commitMedia();
11266
11267 mBIOSSettings->commit();
11268 mVRDEServer->commit();
11269 mAudioAdapter->commit();
11270 mUSBController->commit();
11271 mBandwidthControl->commit();
11272
11273 /* Since mNetworkAdapters is a list which might have been changed (resized)
11274 * without using the Backupable<> template we need to handle the copying
11275 * of the list entries manually, including the creation of peers for the
11276 * new objects. */
11277 bool commitNetworkAdapters = false;
11278 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11279 if (mPeer)
11280 {
11281 /* commit everything, even the ones which will go away */
11282 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11283 mNetworkAdapters[slot]->commit();
11284 /* copy over the new entries, creating a peer and uninit the original */
11285 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11286 for (size_t slot = 0; slot < newSize; slot++)
11287 {
11288 /* look if this adapter has a peer device */
11289 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11290 if (!peer)
11291 {
11292 /* no peer means the adapter is a newly created one;
11293 * create a peer owning data this data share it with */
11294 peer.createObject();
11295 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11296 }
11297 mPeer->mNetworkAdapters[slot] = peer;
11298 }
11299 /* uninit any no longer needed network adapters */
11300 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11301 mNetworkAdapters[slot]->uninit();
11302 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11303 {
11304 if (mPeer->mNetworkAdapters[slot])
11305 mPeer->mNetworkAdapters[slot]->uninit();
11306 }
11307 /* Keep the original network adapter count until this point, so that
11308 * discarding a chipset type change will not lose settings. */
11309 mNetworkAdapters.resize(newSize);
11310 mPeer->mNetworkAdapters.resize(newSize);
11311 }
11312 else
11313 {
11314 /* we have no peer (our parent is the newly created machine);
11315 * just commit changes to the network adapters */
11316 commitNetworkAdapters = true;
11317 }
11318 if (commitNetworkAdapters)
11319 {
11320 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11321 mNetworkAdapters[slot]->commit();
11322 }
11323
11324 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11325 mSerialPorts[slot]->commit();
11326 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11327 mParallelPorts[slot]->commit();
11328
11329 bool commitStorageControllers = false;
11330
11331 if (mStorageControllers.isBackedUp())
11332 {
11333 mStorageControllers.commit();
11334
11335 if (mPeer)
11336 {
11337 /* Commit all changes to new controllers (this will reshare data with
11338 * peers for those who have peers) */
11339 StorageControllerList *newList = new StorageControllerList();
11340 StorageControllerList::const_iterator it = mStorageControllers->begin();
11341 while (it != mStorageControllers->end())
11342 {
11343 (*it)->commit();
11344
11345 /* look if this controller has a peer device */
11346 ComObjPtr<StorageController> peer = (*it)->getPeer();
11347 if (!peer)
11348 {
11349 /* no peer means the device is a newly created one;
11350 * create a peer owning data this device share it with */
11351 peer.createObject();
11352 peer->init(mPeer, *it, true /* aReshare */);
11353 }
11354 else
11355 {
11356 /* remove peer from the old list */
11357 mPeer->mStorageControllers->remove(peer);
11358 }
11359 /* and add it to the new list */
11360 newList->push_back(peer);
11361
11362 ++it;
11363 }
11364
11365 /* uninit old peer's controllers that are left */
11366 it = mPeer->mStorageControllers->begin();
11367 while (it != mPeer->mStorageControllers->end())
11368 {
11369 (*it)->uninit();
11370 ++it;
11371 }
11372
11373 /* attach new list of controllers to our peer */
11374 mPeer->mStorageControllers.attach(newList);
11375 }
11376 else
11377 {
11378 /* we have no peer (our parent is the newly created machine);
11379 * just commit changes to devices */
11380 commitStorageControllers = true;
11381 }
11382 }
11383 else
11384 {
11385 /* the list of controllers itself is not changed,
11386 * just commit changes to controllers themselves */
11387 commitStorageControllers = true;
11388 }
11389
11390 if (commitStorageControllers)
11391 {
11392 StorageControllerList::const_iterator it = mStorageControllers->begin();
11393 while (it != mStorageControllers->end())
11394 {
11395 (*it)->commit();
11396 ++it;
11397 }
11398 }
11399
11400 if (isSessionMachine())
11401 {
11402 /* attach new data to the primary machine and reshare it */
11403 mPeer->mUserData.attach(mUserData);
11404 mPeer->mHWData.attach(mHWData);
11405 /* mMediaData is reshared by fixupMedia */
11406 // mPeer->mMediaData.attach(mMediaData);
11407 Assert(mPeer->mMediaData.data() == mMediaData.data());
11408 }
11409}
11410
11411/**
11412 * Copies all the hardware data from the given machine.
11413 *
11414 * Currently, only called when the VM is being restored from a snapshot. In
11415 * particular, this implies that the VM is not running during this method's
11416 * call.
11417 *
11418 * @note This method must be called from under this object's lock.
11419 *
11420 * @note This method doesn't call #commit(), so all data remains backed up and
11421 * unsaved.
11422 */
11423void Machine::copyFrom(Machine *aThat)
11424{
11425 AssertReturnVoid(!isSnapshotMachine());
11426 AssertReturnVoid(aThat->isSnapshotMachine());
11427
11428 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11429
11430 mHWData.assignCopy(aThat->mHWData);
11431
11432 // create copies of all shared folders (mHWData after attaching a copy
11433 // contains just references to original objects)
11434 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11435 it != mHWData->mSharedFolders.end();
11436 ++it)
11437 {
11438 ComObjPtr<SharedFolder> folder;
11439 folder.createObject();
11440 HRESULT rc = folder->initCopy(getMachine(), *it);
11441 AssertComRC(rc);
11442 *it = folder;
11443 }
11444
11445 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11446 mVRDEServer->copyFrom(aThat->mVRDEServer);
11447 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11448 mUSBController->copyFrom(aThat->mUSBController);
11449 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11450
11451 /* create private copies of all controllers */
11452 mStorageControllers.backup();
11453 mStorageControllers->clear();
11454 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11455 it != aThat->mStorageControllers->end();
11456 ++it)
11457 {
11458 ComObjPtr<StorageController> ctrl;
11459 ctrl.createObject();
11460 ctrl->initCopy(this, *it);
11461 mStorageControllers->push_back(ctrl);
11462 }
11463
11464 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11465 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11466 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11467 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11468 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11469 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11470 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11471}
11472
11473/**
11474 * Returns whether the given storage controller is hotplug capable.
11475 *
11476 * @returns true if the controller supports hotplugging
11477 * false otherwise.
11478 * @param enmCtrlType The controller type to check for.
11479 */
11480bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11481{
11482 switch (enmCtrlType)
11483 {
11484 case StorageControllerType_IntelAhci:
11485 return true;
11486 case StorageControllerType_LsiLogic:
11487 case StorageControllerType_LsiLogicSas:
11488 case StorageControllerType_BusLogic:
11489 case StorageControllerType_PIIX3:
11490 case StorageControllerType_PIIX4:
11491 case StorageControllerType_ICH6:
11492 case StorageControllerType_I82078:
11493 default:
11494 return false;
11495 }
11496}
11497
11498#ifdef VBOX_WITH_RESOURCE_USAGE_API
11499
11500void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11501{
11502 AssertReturnVoid(isWriteLockOnCurrentThread());
11503 AssertPtrReturnVoid(aCollector);
11504
11505 pm::CollectorHAL *hal = aCollector->getHAL();
11506 /* Create sub metrics */
11507 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11508 "Percentage of processor time spent in user mode by the VM process.");
11509 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11510 "Percentage of processor time spent in kernel mode by the VM process.");
11511 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11512 "Size of resident portion of VM process in memory.");
11513 /* Create and register base metrics */
11514 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11515 cpuLoadUser, cpuLoadKernel);
11516 aCollector->registerBaseMetric(cpuLoad);
11517 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11518 ramUsageUsed);
11519 aCollector->registerBaseMetric(ramUsage);
11520
11521 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11522 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11523 new pm::AggregateAvg()));
11524 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11525 new pm::AggregateMin()));
11526 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11527 new pm::AggregateMax()));
11528 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11529 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11530 new pm::AggregateAvg()));
11531 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11532 new pm::AggregateMin()));
11533 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11534 new pm::AggregateMax()));
11535
11536 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11537 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11538 new pm::AggregateAvg()));
11539 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11540 new pm::AggregateMin()));
11541 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11542 new pm::AggregateMax()));
11543
11544
11545 /* Guest metrics collector */
11546 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11547 aCollector->registerGuest(mCollectorGuest);
11548 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11549 this, __PRETTY_FUNCTION__, mCollectorGuest));
11550
11551 /* Create sub metrics */
11552 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11553 "Percentage of processor time spent in user mode as seen by the guest.");
11554 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11555 "Percentage of processor time spent in kernel mode as seen by the guest.");
11556 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11557 "Percentage of processor time spent idling as seen by the guest.");
11558
11559 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11560 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11561 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11562 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11563 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11564 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11565
11566 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11567
11568 /* Create and register base metrics */
11569 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11570 guestLoadUser, guestLoadKernel, guestLoadIdle);
11571 aCollector->registerBaseMetric(guestCpuLoad);
11572
11573 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11574 guestMemTotal, guestMemFree,
11575 guestMemBalloon, guestMemShared,
11576 guestMemCache, guestPagedTotal);
11577 aCollector->registerBaseMetric(guestCpuMem);
11578
11579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11581 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11583
11584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11586 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11588
11589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11593
11594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11596 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11598
11599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11603
11604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11608
11609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11613
11614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11618
11619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11623}
11624
11625void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11626{
11627 AssertReturnVoid(isWriteLockOnCurrentThread());
11628
11629 if (aCollector)
11630 {
11631 aCollector->unregisterMetricsFor(aMachine);
11632 aCollector->unregisterBaseMetricsFor(aMachine);
11633 }
11634}
11635
11636#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11637
11638
11639////////////////////////////////////////////////////////////////////////////////
11640
11641DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11642
11643HRESULT SessionMachine::FinalConstruct()
11644{
11645 LogFlowThisFunc(("\n"));
11646
11647#if defined(RT_OS_WINDOWS)
11648 mIPCSem = NULL;
11649#elif defined(RT_OS_OS2)
11650 mIPCSem = NULLHANDLE;
11651#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11652 mIPCSem = -1;
11653#else
11654# error "Port me!"
11655#endif
11656
11657 return BaseFinalConstruct();
11658}
11659
11660void SessionMachine::FinalRelease()
11661{
11662 LogFlowThisFunc(("\n"));
11663
11664 uninit(Uninit::Unexpected);
11665
11666 BaseFinalRelease();
11667}
11668
11669/**
11670 * @note Must be called only by Machine::openSession() from its own write lock.
11671 */
11672HRESULT SessionMachine::init(Machine *aMachine)
11673{
11674 LogFlowThisFuncEnter();
11675 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11676
11677 AssertReturn(aMachine, E_INVALIDARG);
11678
11679 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11680
11681 /* Enclose the state transition NotReady->InInit->Ready */
11682 AutoInitSpan autoInitSpan(this);
11683 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11684
11685 /* create the interprocess semaphore */
11686#if defined(RT_OS_WINDOWS)
11687 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11688 for (size_t i = 0; i < mIPCSemName.length(); i++)
11689 if (mIPCSemName.raw()[i] == '\\')
11690 mIPCSemName.raw()[i] = '/';
11691 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11692 ComAssertMsgRet(mIPCSem,
11693 ("Cannot create IPC mutex '%ls', err=%d",
11694 mIPCSemName.raw(), ::GetLastError()),
11695 E_FAIL);
11696#elif defined(RT_OS_OS2)
11697 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11698 aMachine->mData->mUuid.raw());
11699 mIPCSemName = ipcSem;
11700 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11701 ComAssertMsgRet(arc == NO_ERROR,
11702 ("Cannot create IPC mutex '%s', arc=%ld",
11703 ipcSem.c_str(), arc),
11704 E_FAIL);
11705#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11706# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11707# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11708 /** @todo Check that this still works correctly. */
11709 AssertCompileSize(key_t, 8);
11710# else
11711 AssertCompileSize(key_t, 4);
11712# endif
11713 key_t key;
11714 mIPCSem = -1;
11715 mIPCKey = "0";
11716 for (uint32_t i = 0; i < 1 << 24; i++)
11717 {
11718 key = ((uint32_t)'V' << 24) | i;
11719 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11720 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11721 {
11722 mIPCSem = sem;
11723 if (sem >= 0)
11724 mIPCKey = BstrFmt("%u", key);
11725 break;
11726 }
11727 }
11728# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11729 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11730 char *pszSemName = NULL;
11731 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11732 key_t key = ::ftok(pszSemName, 'V');
11733 RTStrFree(pszSemName);
11734
11735 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11736# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11737
11738 int errnoSave = errno;
11739 if (mIPCSem < 0 && errnoSave == ENOSYS)
11740 {
11741 setError(E_FAIL,
11742 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11743 "support for SysV IPC. Check the host kernel configuration for "
11744 "CONFIG_SYSVIPC=y"));
11745 return E_FAIL;
11746 }
11747 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11748 * the IPC semaphores */
11749 if (mIPCSem < 0 && errnoSave == ENOSPC)
11750 {
11751#ifdef RT_OS_LINUX
11752 setError(E_FAIL,
11753 tr("Cannot create IPC semaphore because the system limit for the "
11754 "maximum number of semaphore sets (SEMMNI), or the system wide "
11755 "maximum number of semaphores (SEMMNS) would be exceeded. The "
11756 "current set of SysV IPC semaphores can be determined from "
11757 "the file /proc/sysvipc/sem"));
11758#else
11759 setError(E_FAIL,
11760 tr("Cannot create IPC semaphore because the system-imposed limit "
11761 "on the maximum number of allowed semaphores or semaphore "
11762 "identifiers system-wide would be exceeded"));
11763#endif
11764 return E_FAIL;
11765 }
11766 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
11767 E_FAIL);
11768 /* set the initial value to 1 */
11769 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
11770 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
11771 E_FAIL);
11772#else
11773# error "Port me!"
11774#endif
11775
11776 /* memorize the peer Machine */
11777 unconst(mPeer) = aMachine;
11778 /* share the parent pointer */
11779 unconst(mParent) = aMachine->mParent;
11780
11781 /* take the pointers to data to share */
11782 mData.share(aMachine->mData);
11783 mSSData.share(aMachine->mSSData);
11784
11785 mUserData.share(aMachine->mUserData);
11786 mHWData.share(aMachine->mHWData);
11787 mMediaData.share(aMachine->mMediaData);
11788
11789 mStorageControllers.allocate();
11790 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
11791 it != aMachine->mStorageControllers->end();
11792 ++it)
11793 {
11794 ComObjPtr<StorageController> ctl;
11795 ctl.createObject();
11796 ctl->init(this, *it);
11797 mStorageControllers->push_back(ctl);
11798 }
11799
11800 unconst(mBIOSSettings).createObject();
11801 mBIOSSettings->init(this, aMachine->mBIOSSettings);
11802 /* create another VRDEServer object that will be mutable */
11803 unconst(mVRDEServer).createObject();
11804 mVRDEServer->init(this, aMachine->mVRDEServer);
11805 /* create another audio adapter object that will be mutable */
11806 unconst(mAudioAdapter).createObject();
11807 mAudioAdapter->init(this, aMachine->mAudioAdapter);
11808 /* create a list of serial ports that will be mutable */
11809 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11810 {
11811 unconst(mSerialPorts[slot]).createObject();
11812 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
11813 }
11814 /* create a list of parallel ports that will be mutable */
11815 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11816 {
11817 unconst(mParallelPorts[slot]).createObject();
11818 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
11819 }
11820 /* create another USB controller object that will be mutable */
11821 unconst(mUSBController).createObject();
11822 mUSBController->init(this, aMachine->mUSBController);
11823
11824 /* create a list of network adapters that will be mutable */
11825 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
11826 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11827 {
11828 unconst(mNetworkAdapters[slot]).createObject();
11829 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
11830 }
11831
11832 /* create another bandwidth control object that will be mutable */
11833 unconst(mBandwidthControl).createObject();
11834 mBandwidthControl->init(this, aMachine->mBandwidthControl);
11835
11836 /* default is to delete saved state on Saved -> PoweredOff transition */
11837 mRemoveSavedState = true;
11838
11839 /* Confirm a successful initialization when it's the case */
11840 autoInitSpan.setSucceeded();
11841
11842 LogFlowThisFuncLeave();
11843 return S_OK;
11844}
11845
11846/**
11847 * Uninitializes this session object. If the reason is other than
11848 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
11849 *
11850 * @param aReason uninitialization reason
11851 *
11852 * @note Locks mParent + this object for writing.
11853 */
11854void SessionMachine::uninit(Uninit::Reason aReason)
11855{
11856 LogFlowThisFuncEnter();
11857 LogFlowThisFunc(("reason=%d\n", aReason));
11858
11859 /*
11860 * Strongly reference ourselves to prevent this object deletion after
11861 * mData->mSession.mMachine.setNull() below (which can release the last
11862 * reference and call the destructor). Important: this must be done before
11863 * accessing any members (and before AutoUninitSpan that does it as well).
11864 * This self reference will be released as the very last step on return.
11865 */
11866 ComObjPtr<SessionMachine> selfRef = this;
11867
11868 /* Enclose the state transition Ready->InUninit->NotReady */
11869 AutoUninitSpan autoUninitSpan(this);
11870 if (autoUninitSpan.uninitDone())
11871 {
11872 LogFlowThisFunc(("Already uninitialized\n"));
11873 LogFlowThisFuncLeave();
11874 return;
11875 }
11876
11877 if (autoUninitSpan.initFailed())
11878 {
11879 /* We've been called by init() because it's failed. It's not really
11880 * necessary (nor it's safe) to perform the regular uninit sequence
11881 * below, the following is enough.
11882 */
11883 LogFlowThisFunc(("Initialization failed.\n"));
11884#if defined(RT_OS_WINDOWS)
11885 if (mIPCSem)
11886 ::CloseHandle(mIPCSem);
11887 mIPCSem = NULL;
11888#elif defined(RT_OS_OS2)
11889 if (mIPCSem != NULLHANDLE)
11890 ::DosCloseMutexSem(mIPCSem);
11891 mIPCSem = NULLHANDLE;
11892#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11893 if (mIPCSem >= 0)
11894 ::semctl(mIPCSem, 0, IPC_RMID);
11895 mIPCSem = -1;
11896# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11897 mIPCKey = "0";
11898# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
11899#else
11900# error "Port me!"
11901#endif
11902 uninitDataAndChildObjects();
11903 mData.free();
11904 unconst(mParent) = NULL;
11905 unconst(mPeer) = NULL;
11906 LogFlowThisFuncLeave();
11907 return;
11908 }
11909
11910 MachineState_T lastState;
11911 {
11912 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
11913 lastState = mData->mMachineState;
11914 }
11915 NOREF(lastState);
11916
11917#ifdef VBOX_WITH_USB
11918 // release all captured USB devices, but do this before requesting the locks below
11919 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
11920 {
11921 /* Console::captureUSBDevices() is called in the VM process only after
11922 * setting the machine state to Starting or Restoring.
11923 * Console::detachAllUSBDevices() will be called upon successful
11924 * termination. So, we need to release USB devices only if there was
11925 * an abnormal termination of a running VM.
11926 *
11927 * This is identical to SessionMachine::DetachAllUSBDevices except
11928 * for the aAbnormal argument. */
11929 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
11930 AssertComRC(rc);
11931 NOREF(rc);
11932
11933 USBProxyService *service = mParent->host()->usbProxyService();
11934 if (service)
11935 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
11936 }
11937#endif /* VBOX_WITH_USB */
11938
11939 // we need to lock this object in uninit() because the lock is shared
11940 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
11941 // and others need mParent lock, and USB needs host lock.
11942 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
11943
11944#if 0
11945 // Trigger async cleanup tasks, avoid doing things here which are not
11946 // vital to be done immediately and maybe need more locks. This calls
11947 // Machine::unregisterMetrics().
11948 mParent->onMachineUninit(mPeer);
11949#else
11950 /*
11951 * It is safe to call Machine::unregisterMetrics() here because
11952 * PerformanceCollector::samplerCallback no longer accesses guest methods
11953 * holding the lock.
11954 */
11955 unregisterMetrics(mParent->performanceCollector(), mPeer);
11956#endif
11957 /* The guest must be unregistered after its metrics (@bugref{5949}). */
11958 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11959 this, __PRETTY_FUNCTION__, mCollectorGuest));
11960 if (mCollectorGuest)
11961 {
11962 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
11963 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
11964 mCollectorGuest = NULL;
11965 }
11966
11967 if (aReason == Uninit::Abnormal)
11968 {
11969 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
11970 Global::IsOnlineOrTransient(lastState)));
11971
11972 /* reset the state to Aborted */
11973 if (mData->mMachineState != MachineState_Aborted)
11974 setMachineState(MachineState_Aborted);
11975 }
11976
11977 // any machine settings modified?
11978 if (mData->flModifications)
11979 {
11980 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
11981 rollback(false /* aNotify */);
11982 }
11983
11984 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
11985 || !mConsoleTaskData.mSnapshot);
11986 if (!mConsoleTaskData.strStateFilePath.isEmpty())
11987 {
11988 LogWarningThisFunc(("canceling failed save state request!\n"));
11989 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
11990 }
11991 else if (!mConsoleTaskData.mSnapshot.isNull())
11992 {
11993 LogWarningThisFunc(("canceling untaken snapshot!\n"));
11994
11995 /* delete all differencing hard disks created (this will also attach
11996 * their parents back by rolling back mMediaData) */
11997 rollbackMedia();
11998
11999 // delete the saved state file (it might have been already created)
12000 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12001 // think it's still in use
12002 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12003 mConsoleTaskData.mSnapshot->uninit();
12004 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12005 }
12006
12007 if (!mData->mSession.mType.isEmpty())
12008 {
12009 /* mType is not null when this machine's process has been started by
12010 * Machine::LaunchVMProcess(), therefore it is our child. We
12011 * need to queue the PID to reap the process (and avoid zombies on
12012 * Linux). */
12013 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12014 mParent->addProcessToReap(mData->mSession.mPID);
12015 }
12016
12017 mData->mSession.mPID = NIL_RTPROCESS;
12018
12019 if (aReason == Uninit::Unexpected)
12020 {
12021 /* Uninitialization didn't come from #checkForDeath(), so tell the
12022 * client watcher thread to update the set of machines that have open
12023 * sessions. */
12024 mParent->updateClientWatcher();
12025 }
12026
12027 /* uninitialize all remote controls */
12028 if (mData->mSession.mRemoteControls.size())
12029 {
12030 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12031 mData->mSession.mRemoteControls.size()));
12032
12033 Data::Session::RemoteControlList::iterator it =
12034 mData->mSession.mRemoteControls.begin();
12035 while (it != mData->mSession.mRemoteControls.end())
12036 {
12037 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12038 HRESULT rc = (*it)->Uninitialize();
12039 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12040 if (FAILED(rc))
12041 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12042 ++it;
12043 }
12044 mData->mSession.mRemoteControls.clear();
12045 }
12046
12047 /*
12048 * An expected uninitialization can come only from #checkForDeath().
12049 * Otherwise it means that something's gone really wrong (for example,
12050 * the Session implementation has released the VirtualBox reference
12051 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12052 * etc). However, it's also possible, that the client releases the IPC
12053 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12054 * but the VirtualBox release event comes first to the server process.
12055 * This case is practically possible, so we should not assert on an
12056 * unexpected uninit, just log a warning.
12057 */
12058
12059 if ((aReason == Uninit::Unexpected))
12060 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12061
12062 if (aReason != Uninit::Normal)
12063 {
12064 mData->mSession.mDirectControl.setNull();
12065 }
12066 else
12067 {
12068 /* this must be null here (see #OnSessionEnd()) */
12069 Assert(mData->mSession.mDirectControl.isNull());
12070 Assert(mData->mSession.mState == SessionState_Unlocking);
12071 Assert(!mData->mSession.mProgress.isNull());
12072 }
12073 if (mData->mSession.mProgress)
12074 {
12075 if (aReason == Uninit::Normal)
12076 mData->mSession.mProgress->notifyComplete(S_OK);
12077 else
12078 mData->mSession.mProgress->notifyComplete(E_FAIL,
12079 COM_IIDOF(ISession),
12080 getComponentName(),
12081 tr("The VM session was aborted"));
12082 mData->mSession.mProgress.setNull();
12083 }
12084
12085 /* remove the association between the peer machine and this session machine */
12086 Assert( (SessionMachine*)mData->mSession.mMachine == this
12087 || aReason == Uninit::Unexpected);
12088
12089 /* reset the rest of session data */
12090 mData->mSession.mMachine.setNull();
12091 mData->mSession.mState = SessionState_Unlocked;
12092 mData->mSession.mType.setNull();
12093
12094 /* close the interprocess semaphore before leaving the exclusive lock */
12095#if defined(RT_OS_WINDOWS)
12096 if (mIPCSem)
12097 ::CloseHandle(mIPCSem);
12098 mIPCSem = NULL;
12099#elif defined(RT_OS_OS2)
12100 if (mIPCSem != NULLHANDLE)
12101 ::DosCloseMutexSem(mIPCSem);
12102 mIPCSem = NULLHANDLE;
12103#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12104 if (mIPCSem >= 0)
12105 ::semctl(mIPCSem, 0, IPC_RMID);
12106 mIPCSem = -1;
12107# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12108 mIPCKey = "0";
12109# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12110#else
12111# error "Port me!"
12112#endif
12113
12114 /* fire an event */
12115 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12116
12117 uninitDataAndChildObjects();
12118
12119 /* free the essential data structure last */
12120 mData.free();
12121
12122 /* release the exclusive lock before setting the below two to NULL */
12123 multilock.release();
12124
12125 unconst(mParent) = NULL;
12126 unconst(mPeer) = NULL;
12127
12128 LogFlowThisFuncLeave();
12129}
12130
12131// util::Lockable interface
12132////////////////////////////////////////////////////////////////////////////////
12133
12134/**
12135 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12136 * with the primary Machine instance (mPeer).
12137 */
12138RWLockHandle *SessionMachine::lockHandle() const
12139{
12140 AssertReturn(mPeer != NULL, NULL);
12141 return mPeer->lockHandle();
12142}
12143
12144// IInternalMachineControl methods
12145////////////////////////////////////////////////////////////////////////////////
12146
12147/**
12148 * Passes collected guest statistics to performance collector object
12149 */
12150STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser,
12151 ULONG aCpuKernel, ULONG aCpuIdle,
12152 ULONG aMemTotal, ULONG aMemFree,
12153 ULONG aMemBalloon, ULONG aMemShared,
12154 ULONG aMemCache, ULONG aPageTotal,
12155 ULONG aAllocVMM, ULONG aFreeVMM,
12156 ULONG aBalloonedVMM, ULONG aSharedVMM)
12157{
12158 if (mCollectorGuest)
12159 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12160 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12161 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12162 aBalloonedVMM, aSharedVMM);
12163
12164 return S_OK;
12165}
12166
12167/**
12168 * @note Locks this object for writing.
12169 */
12170STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12171{
12172 AutoCaller autoCaller(this);
12173 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12174
12175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12176
12177 mRemoveSavedState = aRemove;
12178
12179 return S_OK;
12180}
12181
12182/**
12183 * @note Locks the same as #setMachineState() does.
12184 */
12185STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12186{
12187 return setMachineState(aMachineState);
12188}
12189
12190/**
12191 * @note Locks this object for reading.
12192 */
12193STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12194{
12195 AutoCaller autoCaller(this);
12196 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12197
12198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12199
12200#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12201 mIPCSemName.cloneTo(aId);
12202 return S_OK;
12203#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12204# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12205 mIPCKey.cloneTo(aId);
12206# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12207 mData->m_strConfigFileFull.cloneTo(aId);
12208# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12209 return S_OK;
12210#else
12211# error "Port me!"
12212#endif
12213}
12214
12215/**
12216 * @note Locks this object for writing.
12217 */
12218STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12219{
12220 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12221 AutoCaller autoCaller(this);
12222 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12223
12224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12225
12226 if (mData->mSession.mState != SessionState_Locked)
12227 return VBOX_E_INVALID_OBJECT_STATE;
12228
12229 if (!mData->mSession.mProgress.isNull())
12230 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12231
12232 LogFlowThisFunc(("returns S_OK.\n"));
12233 return S_OK;
12234}
12235
12236/**
12237 * @note Locks this object for writing.
12238 */
12239STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12240{
12241 AutoCaller autoCaller(this);
12242 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12243
12244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12245
12246 if (mData->mSession.mState != SessionState_Locked)
12247 return VBOX_E_INVALID_OBJECT_STATE;
12248
12249 /* Finalize the LaunchVMProcess progress object. */
12250 if (mData->mSession.mProgress)
12251 {
12252 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12253 mData->mSession.mProgress.setNull();
12254 }
12255
12256 if (SUCCEEDED((HRESULT)iResult))
12257 {
12258#ifdef VBOX_WITH_RESOURCE_USAGE_API
12259 /* The VM has been powered up successfully, so it makes sense
12260 * now to offer the performance metrics for a running machine
12261 * object. Doing it earlier wouldn't be safe. */
12262 registerMetrics(mParent->performanceCollector(), mPeer,
12263 mData->mSession.mPID);
12264#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12265 }
12266
12267 return S_OK;
12268}
12269
12270/**
12271 * @note Locks this object for writing.
12272 */
12273STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12274{
12275 LogFlowThisFuncEnter();
12276
12277 CheckComArgOutPointerValid(aProgress);
12278
12279 AutoCaller autoCaller(this);
12280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12281
12282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12283
12284 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12285 E_FAIL);
12286
12287 /* create a progress object to track operation completion */
12288 ComObjPtr<Progress> pProgress;
12289 pProgress.createObject();
12290 pProgress->init(getVirtualBox(),
12291 static_cast<IMachine *>(this) /* aInitiator */,
12292 Bstr(tr("Stopping the virtual machine")).raw(),
12293 FALSE /* aCancelable */);
12294
12295 /* fill in the console task data */
12296 mConsoleTaskData.mLastState = mData->mMachineState;
12297 mConsoleTaskData.mProgress = pProgress;
12298
12299 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12300 setMachineState(MachineState_Stopping);
12301
12302 pProgress.queryInterfaceTo(aProgress);
12303
12304 return S_OK;
12305}
12306
12307/**
12308 * @note Locks this object for writing.
12309 */
12310STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12311{
12312 LogFlowThisFuncEnter();
12313
12314 AutoCaller autoCaller(this);
12315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12316
12317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12318
12319 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12320 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12321 && mConsoleTaskData.mLastState != MachineState_Null,
12322 E_FAIL);
12323
12324 /*
12325 * On failure, set the state to the state we had when BeginPoweringDown()
12326 * was called (this is expected by Console::PowerDown() and the associated
12327 * task). On success the VM process already changed the state to
12328 * MachineState_PoweredOff, so no need to do anything.
12329 */
12330 if (FAILED(iResult))
12331 setMachineState(mConsoleTaskData.mLastState);
12332
12333 /* notify the progress object about operation completion */
12334 Assert(mConsoleTaskData.mProgress);
12335 if (SUCCEEDED(iResult))
12336 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12337 else
12338 {
12339 Utf8Str strErrMsg(aErrMsg);
12340 if (strErrMsg.length())
12341 mConsoleTaskData.mProgress->notifyComplete(iResult,
12342 COM_IIDOF(ISession),
12343 getComponentName(),
12344 strErrMsg.c_str());
12345 else
12346 mConsoleTaskData.mProgress->notifyComplete(iResult);
12347 }
12348
12349 /* clear out the temporary saved state data */
12350 mConsoleTaskData.mLastState = MachineState_Null;
12351 mConsoleTaskData.mProgress.setNull();
12352
12353 LogFlowThisFuncLeave();
12354 return S_OK;
12355}
12356
12357
12358/**
12359 * Goes through the USB filters of the given machine to see if the given
12360 * device matches any filter or not.
12361 *
12362 * @note Locks the same as USBController::hasMatchingFilter() does.
12363 */
12364STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12365 BOOL *aMatched,
12366 ULONG *aMaskedIfs)
12367{
12368 LogFlowThisFunc(("\n"));
12369
12370 CheckComArgNotNull(aUSBDevice);
12371 CheckComArgOutPointerValid(aMatched);
12372
12373 AutoCaller autoCaller(this);
12374 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12375
12376#ifdef VBOX_WITH_USB
12377 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12378#else
12379 NOREF(aUSBDevice);
12380 NOREF(aMaskedIfs);
12381 *aMatched = FALSE;
12382#endif
12383
12384 return S_OK;
12385}
12386
12387/**
12388 * @note Locks the same as Host::captureUSBDevice() does.
12389 */
12390STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12391{
12392 LogFlowThisFunc(("\n"));
12393
12394 AutoCaller autoCaller(this);
12395 AssertComRCReturnRC(autoCaller.rc());
12396
12397#ifdef VBOX_WITH_USB
12398 /* if captureDeviceForVM() fails, it must have set extended error info */
12399 clearError();
12400 MultiResult rc = mParent->host()->checkUSBProxyService();
12401 if (FAILED(rc)) return rc;
12402
12403 USBProxyService *service = mParent->host()->usbProxyService();
12404 AssertReturn(service, E_FAIL);
12405 return service->captureDeviceForVM(this, Guid(aId).ref());
12406#else
12407 NOREF(aId);
12408 return E_NOTIMPL;
12409#endif
12410}
12411
12412/**
12413 * @note Locks the same as Host::detachUSBDevice() does.
12414 */
12415STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12416{
12417 LogFlowThisFunc(("\n"));
12418
12419 AutoCaller autoCaller(this);
12420 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12421
12422#ifdef VBOX_WITH_USB
12423 USBProxyService *service = mParent->host()->usbProxyService();
12424 AssertReturn(service, E_FAIL);
12425 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12426#else
12427 NOREF(aId);
12428 NOREF(aDone);
12429 return E_NOTIMPL;
12430#endif
12431}
12432
12433/**
12434 * Inserts all machine filters to the USB proxy service and then calls
12435 * Host::autoCaptureUSBDevices().
12436 *
12437 * Called by Console from the VM process upon VM startup.
12438 *
12439 * @note Locks what called methods lock.
12440 */
12441STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12442{
12443 LogFlowThisFunc(("\n"));
12444
12445 AutoCaller autoCaller(this);
12446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12447
12448#ifdef VBOX_WITH_USB
12449 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12450 AssertComRC(rc);
12451 NOREF(rc);
12452
12453 USBProxyService *service = mParent->host()->usbProxyService();
12454 AssertReturn(service, E_FAIL);
12455 return service->autoCaptureDevicesForVM(this);
12456#else
12457 return S_OK;
12458#endif
12459}
12460
12461/**
12462 * Removes all machine filters from the USB proxy service and then calls
12463 * Host::detachAllUSBDevices().
12464 *
12465 * Called by Console from the VM process upon normal VM termination or by
12466 * SessionMachine::uninit() upon abnormal VM termination (from under the
12467 * Machine/SessionMachine lock).
12468 *
12469 * @note Locks what called methods lock.
12470 */
12471STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12472{
12473 LogFlowThisFunc(("\n"));
12474
12475 AutoCaller autoCaller(this);
12476 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12477
12478#ifdef VBOX_WITH_USB
12479 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12480 AssertComRC(rc);
12481 NOREF(rc);
12482
12483 USBProxyService *service = mParent->host()->usbProxyService();
12484 AssertReturn(service, E_FAIL);
12485 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12486#else
12487 NOREF(aDone);
12488 return S_OK;
12489#endif
12490}
12491
12492/**
12493 * @note Locks this object for writing.
12494 */
12495STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12496 IProgress **aProgress)
12497{
12498 LogFlowThisFuncEnter();
12499
12500 AssertReturn(aSession, E_INVALIDARG);
12501 AssertReturn(aProgress, E_INVALIDARG);
12502
12503 AutoCaller autoCaller(this);
12504
12505 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12506 /*
12507 * We don't assert below because it might happen that a non-direct session
12508 * informs us it is closed right after we've been uninitialized -- it's ok.
12509 */
12510 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12511
12512 /* get IInternalSessionControl interface */
12513 ComPtr<IInternalSessionControl> control(aSession);
12514
12515 ComAssertRet(!control.isNull(), E_INVALIDARG);
12516
12517 /* Creating a Progress object requires the VirtualBox lock, and
12518 * thus locking it here is required by the lock order rules. */
12519 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12520
12521 if (control == mData->mSession.mDirectControl)
12522 {
12523 ComAssertRet(aProgress, E_POINTER);
12524
12525 /* The direct session is being normally closed by the client process
12526 * ----------------------------------------------------------------- */
12527
12528 /* go to the closing state (essential for all open*Session() calls and
12529 * for #checkForDeath()) */
12530 Assert(mData->mSession.mState == SessionState_Locked);
12531 mData->mSession.mState = SessionState_Unlocking;
12532
12533 /* set direct control to NULL to release the remote instance */
12534 mData->mSession.mDirectControl.setNull();
12535 LogFlowThisFunc(("Direct control is set to NULL\n"));
12536
12537 if (mData->mSession.mProgress)
12538 {
12539 /* finalize the progress, someone might wait if a frontend
12540 * closes the session before powering on the VM. */
12541 mData->mSession.mProgress->notifyComplete(E_FAIL,
12542 COM_IIDOF(ISession),
12543 getComponentName(),
12544 tr("The VM session was closed before any attempt to power it on"));
12545 mData->mSession.mProgress.setNull();
12546 }
12547
12548 /* Create the progress object the client will use to wait until
12549 * #checkForDeath() is called to uninitialize this session object after
12550 * it releases the IPC semaphore.
12551 * Note! Because we're "reusing" mProgress here, this must be a proxy
12552 * object just like for LaunchVMProcess. */
12553 Assert(mData->mSession.mProgress.isNull());
12554 ComObjPtr<ProgressProxy> progress;
12555 progress.createObject();
12556 ComPtr<IUnknown> pPeer(mPeer);
12557 progress->init(mParent, pPeer,
12558 Bstr(tr("Closing session")).raw(),
12559 FALSE /* aCancelable */);
12560 progress.queryInterfaceTo(aProgress);
12561 mData->mSession.mProgress = progress;
12562 }
12563 else
12564 {
12565 /* the remote session is being normally closed */
12566 Data::Session::RemoteControlList::iterator it =
12567 mData->mSession.mRemoteControls.begin();
12568 while (it != mData->mSession.mRemoteControls.end())
12569 {
12570 if (control == *it)
12571 break;
12572 ++it;
12573 }
12574 BOOL found = it != mData->mSession.mRemoteControls.end();
12575 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12576 E_INVALIDARG);
12577 // This MUST be erase(it), not remove(*it) as the latter triggers a
12578 // very nasty use after free due to the place where the value "lives".
12579 mData->mSession.mRemoteControls.erase(it);
12580 }
12581
12582 /* signal the client watcher thread, because the client is going away */
12583 mParent->updateClientWatcher();
12584
12585 LogFlowThisFuncLeave();
12586 return S_OK;
12587}
12588
12589/**
12590 * @note Locks this object for writing.
12591 */
12592STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12593{
12594 LogFlowThisFuncEnter();
12595
12596 CheckComArgOutPointerValid(aProgress);
12597 CheckComArgOutPointerValid(aStateFilePath);
12598
12599 AutoCaller autoCaller(this);
12600 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12601
12602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12603
12604 AssertReturn( mData->mMachineState == MachineState_Paused
12605 && mConsoleTaskData.mLastState == MachineState_Null
12606 && mConsoleTaskData.strStateFilePath.isEmpty(),
12607 E_FAIL);
12608
12609 /* create a progress object to track operation completion */
12610 ComObjPtr<Progress> pProgress;
12611 pProgress.createObject();
12612 pProgress->init(getVirtualBox(),
12613 static_cast<IMachine *>(this) /* aInitiator */,
12614 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12615 FALSE /* aCancelable */);
12616
12617 Utf8Str strStateFilePath;
12618 /* stateFilePath is null when the machine is not running */
12619 if (mData->mMachineState == MachineState_Paused)
12620 composeSavedStateFilename(strStateFilePath);
12621
12622 /* fill in the console task data */
12623 mConsoleTaskData.mLastState = mData->mMachineState;
12624 mConsoleTaskData.strStateFilePath = strStateFilePath;
12625 mConsoleTaskData.mProgress = pProgress;
12626
12627 /* set the state to Saving (this is expected by Console::SaveState()) */
12628 setMachineState(MachineState_Saving);
12629
12630 strStateFilePath.cloneTo(aStateFilePath);
12631 pProgress.queryInterfaceTo(aProgress);
12632
12633 return S_OK;
12634}
12635
12636/**
12637 * @note Locks mParent + this object for writing.
12638 */
12639STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12640{
12641 LogFlowThisFunc(("\n"));
12642
12643 AutoCaller autoCaller(this);
12644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12645
12646 /* endSavingState() need mParent lock */
12647 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12648
12649 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12650 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12651 && mConsoleTaskData.mLastState != MachineState_Null
12652 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12653 E_FAIL);
12654
12655 /*
12656 * On failure, set the state to the state we had when BeginSavingState()
12657 * was called (this is expected by Console::SaveState() and the associated
12658 * task). On success the VM process already changed the state to
12659 * MachineState_Saved, so no need to do anything.
12660 */
12661 if (FAILED(iResult))
12662 setMachineState(mConsoleTaskData.mLastState);
12663
12664 return endSavingState(iResult, aErrMsg);
12665}
12666
12667/**
12668 * @note Locks this object for writing.
12669 */
12670STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12671{
12672 LogFlowThisFunc(("\n"));
12673
12674 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12675
12676 AutoCaller autoCaller(this);
12677 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12678
12679 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12680
12681 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12682 || mData->mMachineState == MachineState_Teleported
12683 || mData->mMachineState == MachineState_Aborted
12684 , E_FAIL); /** @todo setError. */
12685
12686 Utf8Str stateFilePathFull = aSavedStateFile;
12687 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12688 if (RT_FAILURE(vrc))
12689 return setError(VBOX_E_FILE_ERROR,
12690 tr("Invalid saved state file path '%ls' (%Rrc)"),
12691 aSavedStateFile,
12692 vrc);
12693
12694 mSSData->strStateFilePath = stateFilePathFull;
12695
12696 /* The below setMachineState() will detect the state transition and will
12697 * update the settings file */
12698
12699 return setMachineState(MachineState_Saved);
12700}
12701
12702STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12703 ComSafeArrayOut(BSTR, aValues),
12704 ComSafeArrayOut(LONG64, aTimestamps),
12705 ComSafeArrayOut(BSTR, aFlags))
12706{
12707 LogFlowThisFunc(("\n"));
12708
12709#ifdef VBOX_WITH_GUEST_PROPS
12710 using namespace guestProp;
12711
12712 AutoCaller autoCaller(this);
12713 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12714
12715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12716
12717 CheckComArgOutSafeArrayPointerValid(aNames);
12718 CheckComArgOutSafeArrayPointerValid(aValues);
12719 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12720 CheckComArgOutSafeArrayPointerValid(aFlags);
12721
12722 size_t cEntries = mHWData->mGuestProperties.size();
12723 com::SafeArray<BSTR> names(cEntries);
12724 com::SafeArray<BSTR> values(cEntries);
12725 com::SafeArray<LONG64> timestamps(cEntries);
12726 com::SafeArray<BSTR> flags(cEntries);
12727 unsigned i = 0;
12728 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();
12729 it != mHWData->mGuestProperties.end();
12730 ++it)
12731 {
12732 char szFlags[MAX_FLAGS_LEN + 1];
12733 it->strName.cloneTo(&names[i]);
12734 it->strValue.cloneTo(&values[i]);
12735 timestamps[i] = it->mTimestamp;
12736 /* If it is NULL, keep it NULL. */
12737 if (it->mFlags)
12738 {
12739 writeFlags(it->mFlags, szFlags);
12740 Bstr(szFlags).cloneTo(&flags[i]);
12741 }
12742 else
12743 flags[i] = NULL;
12744 ++i;
12745 }
12746 names.detachTo(ComSafeArrayOutArg(aNames));
12747 values.detachTo(ComSafeArrayOutArg(aValues));
12748 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12749 flags.detachTo(ComSafeArrayOutArg(aFlags));
12750 return S_OK;
12751#else
12752 ReturnComNotImplemented();
12753#endif
12754}
12755
12756STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
12757 IN_BSTR aValue,
12758 LONG64 aTimestamp,
12759 IN_BSTR aFlags)
12760{
12761 LogFlowThisFunc(("\n"));
12762
12763#ifdef VBOX_WITH_GUEST_PROPS
12764 using namespace guestProp;
12765
12766 CheckComArgStrNotEmptyOrNull(aName);
12767 CheckComArgNotNull(aValue);
12768 CheckComArgNotNull(aFlags);
12769
12770 try
12771 {
12772 /*
12773 * Convert input up front.
12774 */
12775 Utf8Str utf8Name(aName);
12776 uint32_t fFlags = NILFLAG;
12777 if (aFlags)
12778 {
12779 Utf8Str utf8Flags(aFlags);
12780 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
12781 AssertRCReturn(vrc, E_INVALIDARG);
12782 }
12783
12784 /*
12785 * Now grab the object lock, validate the state and do the update.
12786 */
12787 AutoCaller autoCaller(this);
12788 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12789
12790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12791
12792 switch (mData->mMachineState)
12793 {
12794 case MachineState_Paused:
12795 case MachineState_Running:
12796 case MachineState_Teleporting:
12797 case MachineState_TeleportingPausedVM:
12798 case MachineState_LiveSnapshotting:
12799 case MachineState_DeletingSnapshotOnline:
12800 case MachineState_DeletingSnapshotPaused:
12801 case MachineState_Saving:
12802 break;
12803
12804 default:
12805#ifndef DEBUG_sunlover
12806 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
12807 VBOX_E_INVALID_VM_STATE);
12808#else
12809 return VBOX_E_INVALID_VM_STATE;
12810#endif
12811 }
12812
12813 setModified(IsModified_MachineData);
12814 mHWData.backup();
12815
12816 /** @todo r=bird: The careful memory handling doesn't work out here because
12817 * the catch block won't undo any damage we've done. So, if push_back throws
12818 * bad_alloc then you've lost the value.
12819 *
12820 * Another thing. Doing a linear search here isn't extremely efficient, esp.
12821 * since values that changes actually bubbles to the end of the list. Using
12822 * something that has an efficient lookup and can tolerate a bit of updates
12823 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some
12824 * combination of RTStrCache (for sharing names and getting uniqueness into
12825 * the bargain) and hash/tree is another. */
12826 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();
12827 iter != mHWData->mGuestProperties.end();
12828 ++iter)
12829 if (utf8Name == iter->strName)
12830 {
12831 mHWData->mGuestProperties.erase(iter);
12832 mData->mGuestPropertiesModified = TRUE;
12833 break;
12834 }
12835 if (aValue != NULL)
12836 {
12837 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };
12838 mHWData->mGuestProperties.push_back(property);
12839 mData->mGuestPropertiesModified = TRUE;
12840 }
12841
12842 /*
12843 * Send a callback notification if appropriate
12844 */
12845 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
12846 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
12847 RTSTR_MAX,
12848 utf8Name.c_str(),
12849 RTSTR_MAX, NULL)
12850 )
12851 {
12852 alock.release();
12853
12854 mParent->onGuestPropertyChange(mData->mUuid,
12855 aName,
12856 aValue,
12857 aFlags);
12858 }
12859 }
12860 catch (...)
12861 {
12862 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
12863 }
12864 return S_OK;
12865#else
12866 ReturnComNotImplemented();
12867#endif
12868}
12869
12870STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
12871 IMediumAttachment **aNewAttachment)
12872{
12873 CheckComArgNotNull(aAttachment);
12874 CheckComArgOutPointerValid(aNewAttachment);
12875
12876 AutoCaller autoCaller(this);
12877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12878
12879 // request the host lock first, since might be calling Host methods for getting host drives;
12880 // next, protect the media tree all the while we're in here, as well as our member variables
12881 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
12882 this->lockHandle(),
12883 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
12884
12885 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
12886
12887 Bstr ctrlName;
12888 LONG lPort;
12889 LONG lDevice;
12890 bool fTempEject;
12891 {
12892 AutoCaller autoAttachCaller(this);
12893 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12894
12895 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12896
12897 /* Need to query the details first, as the IMediumAttachment reference
12898 * might be to the original settings, which we are going to change. */
12899 ctrlName = pAttach->getControllerName();
12900 lPort = pAttach->getPort();
12901 lDevice = pAttach->getDevice();
12902 fTempEject = pAttach->getTempEject();
12903 }
12904
12905 if (!fTempEject)
12906 {
12907 /* Remember previously mounted medium. The medium before taking the
12908 * backup is not necessarily the same thing. */
12909 ComObjPtr<Medium> oldmedium;
12910 oldmedium = pAttach->getMedium();
12911
12912 setModified(IsModified_Storage);
12913 mMediaData.backup();
12914
12915 // The backup operation makes the pAttach reference point to the
12916 // old settings. Re-get the correct reference.
12917 pAttach = findAttachment(mMediaData->mAttachments,
12918 ctrlName.raw(),
12919 lPort,
12920 lDevice);
12921
12922 {
12923 AutoCaller autoAttachCaller(this);
12924 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
12925
12926 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12927 if (!oldmedium.isNull())
12928 oldmedium->removeBackReference(mData->mUuid);
12929
12930 pAttach->updateMedium(NULL);
12931 pAttach->updateEjected();
12932 }
12933
12934 setModified(IsModified_Storage);
12935 }
12936 else
12937 {
12938 {
12939 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
12940 pAttach->updateEjected();
12941 }
12942 }
12943
12944 pAttach.queryInterfaceTo(aNewAttachment);
12945
12946 return S_OK;
12947}
12948
12949// public methods only for internal purposes
12950/////////////////////////////////////////////////////////////////////////////
12951
12952/**
12953 * Called from the client watcher thread to check for expected or unexpected
12954 * death of the client process that has a direct session to this machine.
12955 *
12956 * On Win32 and on OS/2, this method is called only when we've got the
12957 * mutex (i.e. the client has either died or terminated normally) so it always
12958 * returns @c true (the client is terminated, the session machine is
12959 * uninitialized).
12960 *
12961 * On other platforms, the method returns @c true if the client process has
12962 * terminated normally or abnormally and the session machine was uninitialized,
12963 * and @c false if the client process is still alive.
12964 *
12965 * @note Locks this object for writing.
12966 */
12967bool SessionMachine::checkForDeath()
12968{
12969 Uninit::Reason reason;
12970 bool terminated = false;
12971
12972 /* Enclose autoCaller with a block because calling uninit() from under it
12973 * will deadlock. */
12974 {
12975 AutoCaller autoCaller(this);
12976 if (!autoCaller.isOk())
12977 {
12978 /* return true if not ready, to cause the client watcher to exclude
12979 * the corresponding session from watching */
12980 LogFlowThisFunc(("Already uninitialized!\n"));
12981 return true;
12982 }
12983
12984 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12985
12986 /* Determine the reason of death: if the session state is Closing here,
12987 * everything is fine. Otherwise it means that the client did not call
12988 * OnSessionEnd() before it released the IPC semaphore. This may happen
12989 * either because the client process has abnormally terminated, or
12990 * because it simply forgot to call ISession::Close() before exiting. We
12991 * threat the latter also as an abnormal termination (see
12992 * Session::uninit() for details). */
12993 reason = mData->mSession.mState == SessionState_Unlocking ?
12994 Uninit::Normal :
12995 Uninit::Abnormal;
12996
12997#if defined(RT_OS_WINDOWS)
12998
12999 AssertMsg(mIPCSem, ("semaphore must be created"));
13000
13001 /* release the IPC mutex */
13002 ::ReleaseMutex(mIPCSem);
13003
13004 terminated = true;
13005
13006#elif defined(RT_OS_OS2)
13007
13008 AssertMsg(mIPCSem, ("semaphore must be created"));
13009
13010 /* release the IPC mutex */
13011 ::DosReleaseMutexSem(mIPCSem);
13012
13013 terminated = true;
13014
13015#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13016
13017 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13018
13019 int val = ::semctl(mIPCSem, 0, GETVAL);
13020 if (val > 0)
13021 {
13022 /* the semaphore is signaled, meaning the session is terminated */
13023 terminated = true;
13024 }
13025
13026#else
13027# error "Port me!"
13028#endif
13029
13030 } /* AutoCaller block */
13031
13032 if (terminated)
13033 uninit(reason);
13034
13035 return terminated;
13036}
13037
13038/**
13039 * @note Locks this object for reading.
13040 */
13041HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13042{
13043 LogFlowThisFunc(("\n"));
13044
13045 AutoCaller autoCaller(this);
13046 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13047
13048 ComPtr<IInternalSessionControl> directControl;
13049 {
13050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13051 directControl = mData->mSession.mDirectControl;
13052 }
13053
13054 /* ignore notifications sent after #OnSessionEnd() is called */
13055 if (!directControl)
13056 return S_OK;
13057
13058 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13059}
13060
13061/**
13062 * @note Locks this object for reading.
13063 */
13064HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13065 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13066{
13067 LogFlowThisFunc(("\n"));
13068
13069 AutoCaller autoCaller(this);
13070 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13071
13072 ComPtr<IInternalSessionControl> directControl;
13073 {
13074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13075 directControl = mData->mSession.mDirectControl;
13076 }
13077
13078 /* ignore notifications sent after #OnSessionEnd() is called */
13079 if (!directControl)
13080 return S_OK;
13081 /*
13082 * instead acting like callback we ask IVirtualBox deliver corresponding event
13083 */
13084
13085 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13086 return S_OK;
13087}
13088
13089/**
13090 * @note Locks this object for reading.
13091 */
13092HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13093{
13094 LogFlowThisFunc(("\n"));
13095
13096 AutoCaller autoCaller(this);
13097 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13098
13099 ComPtr<IInternalSessionControl> directControl;
13100 {
13101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13102 directControl = mData->mSession.mDirectControl;
13103 }
13104
13105 /* ignore notifications sent after #OnSessionEnd() is called */
13106 if (!directControl)
13107 return S_OK;
13108
13109 return directControl->OnSerialPortChange(serialPort);
13110}
13111
13112/**
13113 * @note Locks this object for reading.
13114 */
13115HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13116{
13117 LogFlowThisFunc(("\n"));
13118
13119 AutoCaller autoCaller(this);
13120 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13121
13122 ComPtr<IInternalSessionControl> directControl;
13123 {
13124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13125 directControl = mData->mSession.mDirectControl;
13126 }
13127
13128 /* ignore notifications sent after #OnSessionEnd() is called */
13129 if (!directControl)
13130 return S_OK;
13131
13132 return directControl->OnParallelPortChange(parallelPort);
13133}
13134
13135/**
13136 * @note Locks this object for reading.
13137 */
13138HRESULT SessionMachine::onStorageControllerChange()
13139{
13140 LogFlowThisFunc(("\n"));
13141
13142 AutoCaller autoCaller(this);
13143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13144
13145 ComPtr<IInternalSessionControl> directControl;
13146 {
13147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13148 directControl = mData->mSession.mDirectControl;
13149 }
13150
13151 /* ignore notifications sent after #OnSessionEnd() is called */
13152 if (!directControl)
13153 return S_OK;
13154
13155 return directControl->OnStorageControllerChange();
13156}
13157
13158/**
13159 * @note Locks this object for reading.
13160 */
13161HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13162{
13163 LogFlowThisFunc(("\n"));
13164
13165 AutoCaller autoCaller(this);
13166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13167
13168 ComPtr<IInternalSessionControl> directControl;
13169 {
13170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13171 directControl = mData->mSession.mDirectControl;
13172 }
13173
13174 /* ignore notifications sent after #OnSessionEnd() is called */
13175 if (!directControl)
13176 return S_OK;
13177
13178 return directControl->OnMediumChange(aAttachment, aForce);
13179}
13180
13181/**
13182 * @note Locks this object for reading.
13183 */
13184HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13185{
13186 LogFlowThisFunc(("\n"));
13187
13188 AutoCaller autoCaller(this);
13189 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13190
13191 ComPtr<IInternalSessionControl> directControl;
13192 {
13193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13194 directControl = mData->mSession.mDirectControl;
13195 }
13196
13197 /* ignore notifications sent after #OnSessionEnd() is called */
13198 if (!directControl)
13199 return S_OK;
13200
13201 return directControl->OnCPUChange(aCPU, aRemove);
13202}
13203
13204HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13205{
13206 LogFlowThisFunc(("\n"));
13207
13208 AutoCaller autoCaller(this);
13209 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13210
13211 ComPtr<IInternalSessionControl> directControl;
13212 {
13213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13214 directControl = mData->mSession.mDirectControl;
13215 }
13216
13217 /* ignore notifications sent after #OnSessionEnd() is called */
13218 if (!directControl)
13219 return S_OK;
13220
13221 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13222}
13223
13224/**
13225 * @note Locks this object for reading.
13226 */
13227HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13228{
13229 LogFlowThisFunc(("\n"));
13230
13231 AutoCaller autoCaller(this);
13232 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13233
13234 ComPtr<IInternalSessionControl> directControl;
13235 {
13236 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13237 directControl = mData->mSession.mDirectControl;
13238 }
13239
13240 /* ignore notifications sent after #OnSessionEnd() is called */
13241 if (!directControl)
13242 return S_OK;
13243
13244 return directControl->OnVRDEServerChange(aRestart);
13245}
13246
13247/**
13248 * @note Locks this object for reading.
13249 */
13250HRESULT SessionMachine::onUSBControllerChange()
13251{
13252 LogFlowThisFunc(("\n"));
13253
13254 AutoCaller autoCaller(this);
13255 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13256
13257 ComPtr<IInternalSessionControl> directControl;
13258 {
13259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13260 directControl = mData->mSession.mDirectControl;
13261 }
13262
13263 /* ignore notifications sent after #OnSessionEnd() is called */
13264 if (!directControl)
13265 return S_OK;
13266
13267 return directControl->OnUSBControllerChange();
13268}
13269
13270/**
13271 * @note Locks this object for reading.
13272 */
13273HRESULT SessionMachine::onSharedFolderChange()
13274{
13275 LogFlowThisFunc(("\n"));
13276
13277 AutoCaller autoCaller(this);
13278 AssertComRCReturnRC(autoCaller.rc());
13279
13280 ComPtr<IInternalSessionControl> directControl;
13281 {
13282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13283 directControl = mData->mSession.mDirectControl;
13284 }
13285
13286 /* ignore notifications sent after #OnSessionEnd() is called */
13287 if (!directControl)
13288 return S_OK;
13289
13290 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13291}
13292
13293/**
13294 * @note Locks this object for reading.
13295 */
13296HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13297{
13298 LogFlowThisFunc(("\n"));
13299
13300 AutoCaller autoCaller(this);
13301 AssertComRCReturnRC(autoCaller.rc());
13302
13303 ComPtr<IInternalSessionControl> directControl;
13304 {
13305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13306 directControl = mData->mSession.mDirectControl;
13307 }
13308
13309 /* ignore notifications sent after #OnSessionEnd() is called */
13310 if (!directControl)
13311 return S_OK;
13312
13313 return directControl->OnClipboardModeChange(aClipboardMode);
13314}
13315
13316/**
13317 * @note Locks this object for reading.
13318 */
13319HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13320{
13321 LogFlowThisFunc(("\n"));
13322
13323 AutoCaller autoCaller(this);
13324 AssertComRCReturnRC(autoCaller.rc());
13325
13326 ComPtr<IInternalSessionControl> directControl;
13327 {
13328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13329 directControl = mData->mSession.mDirectControl;
13330 }
13331
13332 /* ignore notifications sent after #OnSessionEnd() is called */
13333 if (!directControl)
13334 return S_OK;
13335
13336 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13337}
13338
13339/**
13340 * @note Locks this object for reading.
13341 */
13342HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13343{
13344 LogFlowThisFunc(("\n"));
13345
13346 AutoCaller autoCaller(this);
13347 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());
13348
13349 ComPtr<IInternalSessionControl> directControl;
13350 {
13351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13352 directControl = mData->mSession.mDirectControl;
13353 }
13354
13355 /* ignore notifications sent after #OnSessionEnd() is called */
13356 if (!directControl)
13357 return S_OK;
13358
13359 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13360}
13361
13362/**
13363 * @note Locks this object for reading.
13364 */
13365HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
13366{
13367 LogFlowThisFunc(("\n"));
13368
13369 AutoCaller autoCaller(this);
13370 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13371
13372 ComPtr<IInternalSessionControl> directControl;
13373 {
13374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13375 directControl = mData->mSession.mDirectControl;
13376 }
13377
13378 /* ignore notifications sent after #OnSessionEnd() is called */
13379 if (!directControl)
13380 return S_OK;
13381
13382 return directControl->OnStorageDeviceChange(aAttachment, aRemove);
13383}
13384
13385/**
13386 * Returns @c true if this machine's USB controller reports it has a matching
13387 * filter for the given USB device and @c false otherwise.
13388 *
13389 * @note locks this object for reading.
13390 */
13391bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13392{
13393 AutoCaller autoCaller(this);
13394 /* silently return if not ready -- this method may be called after the
13395 * direct machine session has been called */
13396 if (!autoCaller.isOk())
13397 return false;
13398
13399#ifdef VBOX_WITH_USB
13400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13401
13402 switch (mData->mMachineState)
13403 {
13404 case MachineState_Starting:
13405 case MachineState_Restoring:
13406 case MachineState_TeleportingIn:
13407 case MachineState_Paused:
13408 case MachineState_Running:
13409 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13410 * elsewhere... */
13411 alock.release();
13412 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13413 default: break;
13414 }
13415#else
13416 NOREF(aDevice);
13417 NOREF(aMaskedIfs);
13418#endif
13419 return false;
13420}
13421
13422/**
13423 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13424 */
13425HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13426 IVirtualBoxErrorInfo *aError,
13427 ULONG aMaskedIfs)
13428{
13429 LogFlowThisFunc(("\n"));
13430
13431 AutoCaller autoCaller(this);
13432
13433 /* This notification may happen after the machine object has been
13434 * uninitialized (the session was closed), so don't assert. */
13435 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13436
13437 ComPtr<IInternalSessionControl> directControl;
13438 {
13439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13440 directControl = mData->mSession.mDirectControl;
13441 }
13442
13443 /* fail on notifications sent after #OnSessionEnd() is called, it is
13444 * expected by the caller */
13445 if (!directControl)
13446 return E_FAIL;
13447
13448 /* No locks should be held at this point. */
13449 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13450 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13451
13452 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13453}
13454
13455/**
13456 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13457 */
13458HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13459 IVirtualBoxErrorInfo *aError)
13460{
13461 LogFlowThisFunc(("\n"));
13462
13463 AutoCaller autoCaller(this);
13464
13465 /* This notification may happen after the machine object has been
13466 * uninitialized (the session was closed), so don't assert. */
13467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13468
13469 ComPtr<IInternalSessionControl> directControl;
13470 {
13471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13472 directControl = mData->mSession.mDirectControl;
13473 }
13474
13475 /* fail on notifications sent after #OnSessionEnd() is called, it is
13476 * expected by the caller */
13477 if (!directControl)
13478 return E_FAIL;
13479
13480 /* No locks should be held at this point. */
13481 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13482 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13483
13484 return directControl->OnUSBDeviceDetach(aId, aError);
13485}
13486
13487// protected methods
13488/////////////////////////////////////////////////////////////////////////////
13489
13490/**
13491 * Helper method to finalize saving the state.
13492 *
13493 * @note Must be called from under this object's lock.
13494 *
13495 * @param aRc S_OK if the snapshot has been taken successfully
13496 * @param aErrMsg human readable error message for failure
13497 *
13498 * @note Locks mParent + this objects for writing.
13499 */
13500HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13501{
13502 LogFlowThisFuncEnter();
13503
13504 AutoCaller autoCaller(this);
13505 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13506
13507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13508
13509 HRESULT rc = S_OK;
13510
13511 if (SUCCEEDED(aRc))
13512 {
13513 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13514
13515 /* save all VM settings */
13516 rc = saveSettings(NULL);
13517 // no need to check whether VirtualBox.xml needs saving also since
13518 // we can't have a name change pending at this point
13519 }
13520 else
13521 {
13522 // delete the saved state file (it might have been already created);
13523 // we need not check whether this is shared with a snapshot here because
13524 // we certainly created this saved state file here anew
13525 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13526 }
13527
13528 /* notify the progress object about operation completion */
13529 Assert(mConsoleTaskData.mProgress);
13530 if (SUCCEEDED(aRc))
13531 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13532 else
13533 {
13534 if (aErrMsg.length())
13535 mConsoleTaskData.mProgress->notifyComplete(aRc,
13536 COM_IIDOF(ISession),
13537 getComponentName(),
13538 aErrMsg.c_str());
13539 else
13540 mConsoleTaskData.mProgress->notifyComplete(aRc);
13541 }
13542
13543 /* clear out the temporary saved state data */
13544 mConsoleTaskData.mLastState = MachineState_Null;
13545 mConsoleTaskData.strStateFilePath.setNull();
13546 mConsoleTaskData.mProgress.setNull();
13547
13548 LogFlowThisFuncLeave();
13549 return rc;
13550}
13551
13552/**
13553 * Deletes the given file if it is no longer in use by either the current machine state
13554 * (if the machine is "saved") or any of the machine's snapshots.
13555 *
13556 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13557 * but is different for each SnapshotMachine. When calling this, the order of calling this
13558 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13559 * is therefore critical. I know, it's all rather messy.
13560 *
13561 * @param strStateFile
13562 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13563 */
13564void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13565 Snapshot *pSnapshotToIgnore)
13566{
13567 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13568 if ( (strStateFile.isNotEmpty())
13569 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13570 )
13571 // ... and it must also not be shared with other snapshots
13572 if ( !mData->mFirstSnapshot
13573 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13574 // this checks the SnapshotMachine's state file paths
13575 )
13576 RTFileDelete(strStateFile.c_str());
13577}
13578
13579/**
13580 * Locks the attached media.
13581 *
13582 * All attached hard disks are locked for writing and DVD/floppy are locked for
13583 * reading. Parents of attached hard disks (if any) are locked for reading.
13584 *
13585 * This method also performs accessibility check of all media it locks: if some
13586 * media is inaccessible, the method will return a failure and a bunch of
13587 * extended error info objects per each inaccessible medium.
13588 *
13589 * Note that this method is atomic: if it returns a success, all media are
13590 * locked as described above; on failure no media is locked at all (all
13591 * succeeded individual locks will be undone).
13592 *
13593 * This method is intended to be called when the machine is in Starting or
13594 * Restoring state and asserts otherwise.
13595 *
13596 * The locks made by this method must be undone by calling #unlockMedia() when
13597 * no more needed.
13598 */
13599HRESULT SessionMachine::lockMedia()
13600{
13601 AutoCaller autoCaller(this);
13602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13603
13604 AutoMultiWriteLock2 alock(this->lockHandle(),
13605 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13606
13607 AssertReturn( mData->mMachineState == MachineState_Starting
13608 || mData->mMachineState == MachineState_Restoring
13609 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13610 /* bail out if trying to lock things with already set up locking */
13611 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13612
13613 clearError();
13614 MultiResult mrc(S_OK);
13615
13616 /* Collect locking information for all medium objects attached to the VM. */
13617 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13618 it != mMediaData->mAttachments.end();
13619 ++it)
13620 {
13621 MediumAttachment* pAtt = *it;
13622 DeviceType_T devType = pAtt->getType();
13623 Medium *pMedium = pAtt->getMedium();
13624
13625 MediumLockList *pMediumLockList(new MediumLockList());
13626 // There can be attachments without a medium (floppy/dvd), and thus
13627 // it's impossible to create a medium lock list. It still makes sense
13628 // to have the empty medium lock list in the map in case a medium is
13629 // attached later.
13630 if (pMedium != NULL)
13631 {
13632 MediumType_T mediumType = pMedium->getType();
13633 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13634 || mediumType == MediumType_Shareable;
13635 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13636
13637 alock.release();
13638 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13639 !fIsReadOnlyLock /* fMediumLockWrite */,
13640 NULL,
13641 *pMediumLockList);
13642 alock.acquire();
13643 if (FAILED(mrc))
13644 {
13645 delete pMediumLockList;
13646 mData->mSession.mLockedMedia.Clear();
13647 break;
13648 }
13649 }
13650
13651 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13652 if (FAILED(rc))
13653 {
13654 mData->mSession.mLockedMedia.Clear();
13655 mrc = setError(rc,
13656 tr("Collecting locking information for all attached media failed"));
13657 break;
13658 }
13659 }
13660
13661 if (SUCCEEDED(mrc))
13662 {
13663 /* Now lock all media. If this fails, nothing is locked. */
13664 alock.release();
13665 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13666 alock.acquire();
13667 if (FAILED(rc))
13668 {
13669 mrc = setError(rc,
13670 tr("Locking of attached media failed"));
13671 }
13672 }
13673
13674 return mrc;
13675}
13676
13677/**
13678 * Undoes the locks made by by #lockMedia().
13679 */
13680void SessionMachine::unlockMedia()
13681{
13682 AutoCaller autoCaller(this);
13683 AssertComRCReturnVoid(autoCaller.rc());
13684
13685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13686
13687 /* we may be holding important error info on the current thread;
13688 * preserve it */
13689 ErrorInfoKeeper eik;
13690
13691 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13692 AssertComRC(rc);
13693}
13694
13695/**
13696 * Helper to change the machine state (reimplementation).
13697 *
13698 * @note Locks this object for writing.
13699 * @note This method must not call saveSettings or SaveSettings, otherwise
13700 * it can cause crashes in random places due to unexpectedly committing
13701 * the current settings. The caller is responsible for that. The call
13702 * to saveStateSettings is fine, because this method does not commit.
13703 */
13704HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13705{
13706 LogFlowThisFuncEnter();
13707 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13708
13709 AutoCaller autoCaller(this);
13710 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13711
13712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13713
13714 MachineState_T oldMachineState = mData->mMachineState;
13715
13716 AssertMsgReturn(oldMachineState != aMachineState,
13717 ("oldMachineState=%s, aMachineState=%s\n",
13718 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13719 E_FAIL);
13720
13721 HRESULT rc = S_OK;
13722
13723 int stsFlags = 0;
13724 bool deleteSavedState = false;
13725
13726 /* detect some state transitions */
13727
13728 if ( ( oldMachineState == MachineState_Saved
13729 && aMachineState == MachineState_Restoring)
13730 || ( ( oldMachineState == MachineState_PoweredOff
13731 || oldMachineState == MachineState_Teleported
13732 || oldMachineState == MachineState_Aborted
13733 )
13734 && ( aMachineState == MachineState_TeleportingIn
13735 || aMachineState == MachineState_Starting
13736 )
13737 )
13738 )
13739 {
13740 /* The EMT thread is about to start */
13741
13742 /* Nothing to do here for now... */
13743
13744 /// @todo NEWMEDIA don't let mDVDDrive and other children
13745 /// change anything when in the Starting/Restoring state
13746 }
13747 else if ( ( oldMachineState == MachineState_Running
13748 || oldMachineState == MachineState_Paused
13749 || oldMachineState == MachineState_Teleporting
13750 || oldMachineState == MachineState_LiveSnapshotting
13751 || oldMachineState == MachineState_Stuck
13752 || oldMachineState == MachineState_Starting
13753 || oldMachineState == MachineState_Stopping
13754 || oldMachineState == MachineState_Saving
13755 || oldMachineState == MachineState_Restoring
13756 || oldMachineState == MachineState_TeleportingPausedVM
13757 || oldMachineState == MachineState_TeleportingIn
13758 )
13759 && ( aMachineState == MachineState_PoweredOff
13760 || aMachineState == MachineState_Saved
13761 || aMachineState == MachineState_Teleported
13762 || aMachineState == MachineState_Aborted
13763 )
13764 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
13765 * snapshot */
13766 && ( mConsoleTaskData.mSnapshot.isNull()
13767 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
13768 )
13769 )
13770 {
13771 /* The EMT thread has just stopped, unlock attached media. Note that as
13772 * opposed to locking that is done from Console, we do unlocking here
13773 * because the VM process may have aborted before having a chance to
13774 * properly unlock all media it locked. */
13775
13776 unlockMedia();
13777 }
13778
13779 if (oldMachineState == MachineState_Restoring)
13780 {
13781 if (aMachineState != MachineState_Saved)
13782 {
13783 /*
13784 * delete the saved state file once the machine has finished
13785 * restoring from it (note that Console sets the state from
13786 * Restoring to Saved if the VM couldn't restore successfully,
13787 * to give the user an ability to fix an error and retry --
13788 * we keep the saved state file in this case)
13789 */
13790 deleteSavedState = true;
13791 }
13792 }
13793 else if ( oldMachineState == MachineState_Saved
13794 && ( aMachineState == MachineState_PoweredOff
13795 || aMachineState == MachineState_Aborted
13796 || aMachineState == MachineState_Teleported
13797 )
13798 )
13799 {
13800 /*
13801 * delete the saved state after Console::ForgetSavedState() is called
13802 * or if the VM process (owning a direct VM session) crashed while the
13803 * VM was Saved
13804 */
13805
13806 /// @todo (dmik)
13807 // Not sure that deleting the saved state file just because of the
13808 // client death before it attempted to restore the VM is a good
13809 // thing. But when it crashes we need to go to the Aborted state
13810 // which cannot have the saved state file associated... The only
13811 // way to fix this is to make the Aborted condition not a VM state
13812 // but a bool flag: i.e., when a crash occurs, set it to true and
13813 // change the state to PoweredOff or Saved depending on the
13814 // saved state presence.
13815
13816 deleteSavedState = true;
13817 mData->mCurrentStateModified = TRUE;
13818 stsFlags |= SaveSTS_CurStateModified;
13819 }
13820
13821 if ( aMachineState == MachineState_Starting
13822 || aMachineState == MachineState_Restoring
13823 || aMachineState == MachineState_TeleportingIn
13824 )
13825 {
13826 /* set the current state modified flag to indicate that the current
13827 * state is no more identical to the state in the
13828 * current snapshot */
13829 if (!mData->mCurrentSnapshot.isNull())
13830 {
13831 mData->mCurrentStateModified = TRUE;
13832 stsFlags |= SaveSTS_CurStateModified;
13833 }
13834 }
13835
13836 if (deleteSavedState)
13837 {
13838 if (mRemoveSavedState)
13839 {
13840 Assert(!mSSData->strStateFilePath.isEmpty());
13841
13842 // it is safe to delete the saved state file if ...
13843 if ( !mData->mFirstSnapshot // ... we have no snapshots or
13844 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
13845 // ... none of the snapshots share the saved state file
13846 )
13847 RTFileDelete(mSSData->strStateFilePath.c_str());
13848 }
13849
13850 mSSData->strStateFilePath.setNull();
13851 stsFlags |= SaveSTS_StateFilePath;
13852 }
13853
13854 /* redirect to the underlying peer machine */
13855 mPeer->setMachineState(aMachineState);
13856
13857 if ( aMachineState == MachineState_PoweredOff
13858 || aMachineState == MachineState_Teleported
13859 || aMachineState == MachineState_Aborted
13860 || aMachineState == MachineState_Saved)
13861 {
13862 /* the machine has stopped execution
13863 * (or the saved state file was adopted) */
13864 stsFlags |= SaveSTS_StateTimeStamp;
13865 }
13866
13867 if ( ( oldMachineState == MachineState_PoweredOff
13868 || oldMachineState == MachineState_Aborted
13869 || oldMachineState == MachineState_Teleported
13870 )
13871 && aMachineState == MachineState_Saved)
13872 {
13873 /* the saved state file was adopted */
13874 Assert(!mSSData->strStateFilePath.isEmpty());
13875 stsFlags |= SaveSTS_StateFilePath;
13876 }
13877
13878#ifdef VBOX_WITH_GUEST_PROPS
13879 if ( aMachineState == MachineState_PoweredOff
13880 || aMachineState == MachineState_Aborted
13881 || aMachineState == MachineState_Teleported)
13882 {
13883 /* Make sure any transient guest properties get removed from the
13884 * property store on shutdown. */
13885
13886 HWData::GuestPropertyList::iterator it;
13887 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
13888 if (!fNeedsSaving)
13889 for (it = mHWData->mGuestProperties.begin();
13890 it != mHWData->mGuestProperties.end(); ++it)
13891 if ( (it->mFlags & guestProp::TRANSIENT)
13892 || (it->mFlags & guestProp::TRANSRESET))
13893 {
13894 fNeedsSaving = true;
13895 break;
13896 }
13897 if (fNeedsSaving)
13898 {
13899 mData->mCurrentStateModified = TRUE;
13900 stsFlags |= SaveSTS_CurStateModified;
13901 }
13902 }
13903#endif
13904
13905 rc = saveStateSettings(stsFlags);
13906
13907 if ( ( oldMachineState != MachineState_PoweredOff
13908 && oldMachineState != MachineState_Aborted
13909 && oldMachineState != MachineState_Teleported
13910 )
13911 && ( aMachineState == MachineState_PoweredOff
13912 || aMachineState == MachineState_Aborted
13913 || aMachineState == MachineState_Teleported
13914 )
13915 )
13916 {
13917 /* we've been shut down for any reason */
13918 /* no special action so far */
13919 }
13920
13921 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
13922 LogFlowThisFuncLeave();
13923 return rc;
13924}
13925
13926/**
13927 * Sends the current machine state value to the VM process.
13928 *
13929 * @note Locks this object for reading, then calls a client process.
13930 */
13931HRESULT SessionMachine::updateMachineStateOnClient()
13932{
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13935
13936 ComPtr<IInternalSessionControl> directControl;
13937 {
13938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13939 AssertReturn(!!mData, E_FAIL);
13940 directControl = mData->mSession.mDirectControl;
13941
13942 /* directControl may be already set to NULL here in #OnSessionEnd()
13943 * called too early by the direct session process while there is still
13944 * some operation (like deleting the snapshot) in progress. The client
13945 * process in this case is waiting inside Session::close() for the
13946 * "end session" process object to complete, while #uninit() called by
13947 * #checkForDeath() on the Watcher thread is waiting for the pending
13948 * operation to complete. For now, we accept this inconsistent behavior
13949 * and simply do nothing here. */
13950
13951 if (mData->mSession.mState == SessionState_Unlocking)
13952 return S_OK;
13953
13954 AssertReturn(!directControl.isNull(), E_FAIL);
13955 }
13956
13957 return directControl->UpdateMachineState(mData->mMachineState);
13958}
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