VirtualBox

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

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

Main: Introducing CPUPropertyType_LongMode + legacy band aid.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 476.9 KB
Line 
1/* $Id: MachineImpl.cpp 45622 2013-04-18 21:49:05Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER
27# include <errno.h>
28# include <sys/types.h>
29# include <sys/stat.h>
30# include <sys/ipc.h>
31# include <sys/sem.h>
32#endif
33
34#include "Logging.h"
35#include "VirtualBoxImpl.h"
36#include "MachineImpl.h"
37#include "ProgressImpl.h"
38#include "ProgressProxyImpl.h"
39#include "MediumAttachmentImpl.h"
40#include "MediumImpl.h"
41#include "MediumLock.h"
42#include "USBControllerImpl.h"
43#include "HostImpl.h"
44#include "SharedFolderImpl.h"
45#include "GuestOSTypeImpl.h"
46#include "VirtualBoxErrorInfoImpl.h"
47#include "GuestImpl.h"
48#include "StorageControllerImpl.h"
49#include "DisplayImpl.h"
50#include "DisplayUtils.h"
51#include "BandwidthControlImpl.h"
52#include "MachineImplCloneVM.h"
53#include "AutostartDb.h"
54
55// generated header
56#include "VBoxEvents.h"
57
58#ifdef VBOX_WITH_USB
59# include "USBProxyService.h"
60#endif
61
62#include "AutoCaller.h"
63#include "HashedPw.h"
64#include "Performance.h"
65
66#include <iprt/asm.h>
67#if HC_ARCH_BITS == 32
68# include <iprt/asm-amd64-x86.h>
69#endif
70#include <iprt/path.h>
71#include <iprt/dir.h>
72#include <iprt/env.h>
73#include <iprt/lockvalidator.h>
74#include <iprt/process.h>
75#include <iprt/cpp/utils.h>
76#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
77#include <iprt/sha.h>
78#include <iprt/string.h>
79
80#include <VBox/com/array.h>
81#include <VBox/com/list.h>
82
83#include <VBox/err.h>
84#include <VBox/param.h>
85#include <VBox/settings.h>
86#include <VBox/vmm/ssm.h>
87
88#ifdef VBOX_WITH_GUEST_PROPS
89# include <VBox/HostServices/GuestPropertySvc.h>
90# include <VBox/com/array.h>
91#endif
92
93#include "VBox/com/MultiResult.h"
94
95#include <algorithm>
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mState = SessionState_Unlocked;
138}
139
140Machine::Data::~Data()
141{
142 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
143 {
144 RTSemEventMultiDestroy(mMachineStateDepsSem);
145 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
146 }
147 if (pMachineConfigFile)
148 {
149 delete pMachineConfigFile;
150 pMachineConfigFile = NULL;
151 }
152}
153
154/////////////////////////////////////////////////////////////////////////////
155// Machine::HWData structure
156/////////////////////////////////////////////////////////////////////////////
157
158Machine::HWData::HWData()
159{
160 /* default values for a newly created machine */
161 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
162 mMemorySize = 128;
163 mCPUCount = 1;
164 mCPUHotPlugEnabled = false;
165 mMemoryBalloonSize = 0;
166 mPageFusionEnabled = false;
167 mVRAMSize = 8;
168 mAccelerate3DEnabled = false;
169 mAccelerate2DVideoEnabled = false;
170 mMonitorCount = 1;
171 mVideoCaptureFile = "Test.webm";
172 mVideoCaptureWidth = 640;
173 mVideoCaptureHeight = 480;
174 mVideoCaptureEnabled = false;
175
176 mHWVirtExEnabled = true;
177 mHWVirtExNestedPagingEnabled = true;
178#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
179 mHWVirtExLargePagesEnabled = true;
180#else
181 /* Not supported on 32 bits hosts. */
182 mHWVirtExLargePagesEnabled = false;
183#endif
184 mHWVirtExVPIDEnabled = true;
185 mHWVirtExForceEnabled = false;
186#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
187 mHWVirtExExclusive = false;
188#else
189 mHWVirtExExclusive = true;
190#endif
191#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
192 mPAEEnabled = true;
193#else
194 mPAEEnabled = false;
195#endif
196 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
197 mSyntheticCpu = false;
198 mHPETEnabled = false;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDragAndDropMode = DragAndDropMode_Disabled;
209 mGuestPropertyNotificationPatterns = "";
210
211 mFirmwareType = FirmwareType_BIOS;
212 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
213 mPointingHIDType = PointingHIDType_PS2Mouse;
214 mChipsetType = ChipsetType_PIIX3;
215 mEmulatedUSBWebcamEnabled = FALSE;
216 mEmulatedUSBCardReaderEnabled = FALSE;
217
218 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
219 mCPUAttached[i] = false;
220
221 mIOCacheEnabled = true;
222 mIOCacheSize = 5; /* 5MB */
223
224 /* Maximum CPU execution cap by default. */
225 mCpuExecutionCap = 100;
226}
227
228Machine::HWData::~HWData()
229{
230}
231
232/////////////////////////////////////////////////////////////////////////////
233// Machine::HDData structure
234/////////////////////////////////////////////////////////////////////////////
235
236Machine::MediaData::MediaData()
237{
238}
239
240Machine::MediaData::~MediaData()
241{
242}
243
244/////////////////////////////////////////////////////////////////////////////
245// Machine class
246/////////////////////////////////////////////////////////////////////////////
247
248// constructor / destructor
249/////////////////////////////////////////////////////////////////////////////
250
251Machine::Machine()
252 : mCollectorGuest(NULL),
253 mPeer(NULL),
254 mParent(NULL),
255 mSerialPorts(),
256 mParallelPorts(),
257 uRegistryNeedsSaving(0)
258{}
259
260Machine::~Machine()
261{}
262
263HRESULT Machine::FinalConstruct()
264{
265 LogFlowThisFunc(("\n"));
266 return BaseFinalConstruct();
267}
268
269void Machine::FinalRelease()
270{
271 LogFlowThisFunc(("\n"));
272 uninit();
273 BaseFinalRelease();
274}
275
276/**
277 * Initializes a new machine instance; this init() variant creates a new, empty machine.
278 * This gets called from VirtualBox::CreateMachine().
279 *
280 * @param aParent Associated parent object
281 * @param strConfigFile Local file system path to the VM settings file (can
282 * be relative to the VirtualBox config directory).
283 * @param strName name for the machine
284 * @param llGroups list of groups for the machine
285 * @param aOsType OS Type of this machine or NULL.
286 * @param aId UUID for the new machine.
287 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
288 *
289 * @return Success indicator. if not S_OK, the machine object is invalid
290 */
291HRESULT Machine::init(VirtualBox *aParent,
292 const Utf8Str &strConfigFile,
293 const Utf8Str &strName,
294 const StringsList &llGroups,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->id();
346
347 /* Apply BIOS defaults */
348 mBIOSSettings->applyDefaults(aOsType);
349
350 /* Apply network adapters defaults */
351 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
352 mNetworkAdapters[slot]->applyDefaults(aOsType);
353
354 /* Apply serial port defaults */
355 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
356 mSerialPorts[slot]->applyDefaults(aOsType);
357
358 /* Let the OS type select 64-bit ness. */
359 mHWData->mLongMode = aOsType->is64Bit()
360 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
361 }
362
363 /* At this point the changing of the current state modification
364 * flag is allowed. */
365 allowStateModification();
366
367 /* commit all changes made during the initialization */
368 commit();
369 }
370
371 /* Confirm a successful initialization when it's the case */
372 if (SUCCEEDED(rc))
373 {
374 if (mData->mAccessible)
375 autoInitSpan.setSucceeded();
376 else
377 autoInitSpan.setLimited();
378 }
379
380 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
381 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
382 mData->mRegistered,
383 mData->mAccessible,
384 rc));
385
386 LogFlowThisFuncLeave();
387
388 return rc;
389}
390
391/**
392 * Initializes a new instance with data from machine XML (formerly Init_Registered).
393 * Gets called in two modes:
394 *
395 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
396 * UUID is specified and we mark the machine as "registered";
397 *
398 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
399 * and the machine remains unregistered until RegisterMachine() is called.
400 *
401 * @param aParent Associated parent object
402 * @param aConfigFile Local file system path to the VM settings file (can
403 * be relative to the VirtualBox config directory).
404 * @param aId UUID of the machine or NULL (see above).
405 *
406 * @return Success indicator. if not S_OK, the machine object is invalid
407 */
408HRESULT Machine::initFromSettings(VirtualBox *aParent,
409 const Utf8Str &strConfigFile,
410 const Guid *aId)
411{
412 LogFlowThisFuncEnter();
413 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
414
415 /* Enclose the state transition NotReady->InInit->Ready */
416 AutoInitSpan autoInitSpan(this);
417 AssertReturn(autoInitSpan.isOk(), E_FAIL);
418
419 HRESULT rc = initImpl(aParent, strConfigFile);
420 if (FAILED(rc)) return rc;
421
422 if (aId)
423 {
424 // loading a registered VM:
425 unconst(mData->mUuid) = *aId;
426 mData->mRegistered = TRUE;
427 // now load the settings from XML:
428 rc = registeredInit();
429 // this calls initDataAndChildObjects() and loadSettings()
430 }
431 else
432 {
433 // opening an unregistered VM (VirtualBox::OpenMachine()):
434 rc = initDataAndChildObjects();
435
436 if (SUCCEEDED(rc))
437 {
438 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
439 mData->mAccessible = TRUE;
440
441 try
442 {
443 // load and parse machine XML; this will throw on XML or logic errors
444 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
445
446 // reject VM UUID duplicates, they can happen if someone
447 // tries to register an already known VM config again
448 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
449 true /* fPermitInaccessible */,
450 false /* aDoSetError */,
451 NULL) != VBOX_E_OBJECT_NOT_FOUND)
452 {
453 throw setError(E_FAIL,
454 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
455 mData->m_strConfigFile.c_str());
456 }
457
458 // use UUID from machine config
459 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
460
461 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
462 NULL /* puuidRegistry */);
463 if (FAILED(rc)) throw rc;
464
465 /* At this point the changing of the current state modification
466 * flag is allowed. */
467 allowStateModification();
468
469 commit();
470 }
471 catch (HRESULT err)
472 {
473 /* we assume that error info is set by the thrower */
474 rc = err;
475 }
476 catch (...)
477 {
478 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
479 }
480 }
481 }
482
483 /* Confirm a successful initialization when it's the case */
484 if (SUCCEEDED(rc))
485 {
486 if (mData->mAccessible)
487 autoInitSpan.setSucceeded();
488 else
489 {
490 autoInitSpan.setLimited();
491
492 // uninit media from this machine's media registry, or else
493 // reloading the settings will fail
494 mParent->unregisterMachineMedia(getId());
495 }
496 }
497
498 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
499 "rc=%08X\n",
500 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
501 mData->mRegistered, mData->mAccessible, rc));
502
503 LogFlowThisFuncLeave();
504
505 return rc;
506}
507
508/**
509 * Initializes a new instance from a machine config that is already in memory
510 * (import OVF case). Since we are importing, the UUID in the machine
511 * config is ignored and we always generate a fresh one.
512 *
513 * @param strName Name for the new machine; this overrides what is specified in config and is used
514 * for the settings file as well.
515 * @param config Machine configuration loaded and parsed from XML.
516 *
517 * @return Success indicator. if not S_OK, the machine object is invalid
518 */
519HRESULT Machine::init(VirtualBox *aParent,
520 const Utf8Str &strName,
521 const settings::MachineConfigFile &config)
522{
523 LogFlowThisFuncEnter();
524
525 /* Enclose the state transition NotReady->InInit->Ready */
526 AutoInitSpan autoInitSpan(this);
527 AssertReturn(autoInitSpan.isOk(), E_FAIL);
528
529 Utf8Str strConfigFile;
530 aParent->getDefaultMachineFolder(strConfigFile);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(RTPATH_DELIMITER);
534 strConfigFile.append(strName);
535 strConfigFile.append(".vbox");
536
537 HRESULT rc = initImpl(aParent, strConfigFile);
538 if (FAILED(rc)) return rc;
539
540 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
541 if (FAILED(rc)) return rc;
542
543 rc = initDataAndChildObjects();
544
545 if (SUCCEEDED(rc))
546 {
547 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
548 mData->mAccessible = TRUE;
549
550 // create empty machine config for instance data
551 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
552
553 // generate fresh UUID, ignore machine config
554 unconst(mData->mUuid).create();
555
556 rc = loadMachineDataFromSettings(config,
557 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
558
559 // override VM name as well, it may be different
560 mUserData->s.strName = strName;
561
562 if (SUCCEEDED(rc))
563 {
564 /* At this point the changing of the current state modification
565 * flag is allowed. */
566 allowStateModification();
567
568 /* commit all changes made during the initialization */
569 commit();
570 }
571 }
572
573 /* Confirm a successful initialization when it's the case */
574 if (SUCCEEDED(rc))
575 {
576 if (mData->mAccessible)
577 autoInitSpan.setSucceeded();
578 else
579 {
580 autoInitSpan.setLimited();
581
582 // uninit media from this machine's media registry, or else
583 // reloading the settings will fail
584 mParent->unregisterMachineMedia(getId());
585 }
586 }
587
588 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
589 "rc=%08X\n",
590 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
591 mData->mRegistered, mData->mAccessible, rc));
592
593 LogFlowThisFuncLeave();
594
595 return rc;
596}
597
598/**
599 * Shared code between the various init() implementations.
600 * @param aParent
601 * @return
602 */
603HRESULT Machine::initImpl(VirtualBox *aParent,
604 const Utf8Str &strConfigFile)
605{
606 LogFlowThisFuncEnter();
607
608 AssertReturn(aParent, E_INVALIDARG);
609 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
610
611 HRESULT rc = S_OK;
612
613 /* share the parent weakly */
614 unconst(mParent) = aParent;
615
616 /* allocate the essential machine data structure (the rest will be
617 * allocated later by initDataAndChildObjects() */
618 mData.allocate();
619
620 /* memorize the config file name (as provided) */
621 mData->m_strConfigFile = strConfigFile;
622
623 /* get the full file name */
624 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
625 if (RT_FAILURE(vrc1))
626 return setError(VBOX_E_FILE_ERROR,
627 tr("Invalid machine settings file name '%s' (%Rrc)"),
628 strConfigFile.c_str(),
629 vrc1);
630
631 LogFlowThisFuncLeave();
632
633 return rc;
634}
635
636/**
637 * Tries to create a machine settings file in the path stored in the machine
638 * instance data. Used when a new machine is created to fail gracefully if
639 * the settings file could not be written (e.g. because machine dir is read-only).
640 * @return
641 */
642HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
643{
644 HRESULT rc = S_OK;
645
646 // when we create a new machine, we must be able to create the settings file
647 RTFILE f = NIL_RTFILE;
648 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
649 if ( RT_SUCCESS(vrc)
650 || vrc == VERR_SHARING_VIOLATION
651 )
652 {
653 if (RT_SUCCESS(vrc))
654 RTFileClose(f);
655 if (!fForceOverwrite)
656 rc = setError(VBOX_E_FILE_ERROR,
657 tr("Machine settings file '%s' already exists"),
658 mData->m_strConfigFileFull.c_str());
659 else
660 {
661 /* try to delete the config file, as otherwise the creation
662 * of a new settings file will fail. */
663 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
664 if (RT_FAILURE(vrc2))
665 rc = setError(VBOX_E_FILE_ERROR,
666 tr("Could not delete the existing settings file '%s' (%Rrc)"),
667 mData->m_strConfigFileFull.c_str(), vrc2);
668 }
669 }
670 else if ( vrc != VERR_FILE_NOT_FOUND
671 && vrc != VERR_PATH_NOT_FOUND
672 )
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Invalid machine settings file name '%s' (%Rrc)"),
675 mData->m_strConfigFileFull.c_str(),
676 vrc);
677 return rc;
678}
679
680/**
681 * Initializes the registered machine by loading the settings file.
682 * This method is separated from #init() in order to make it possible to
683 * retry the operation after VirtualBox startup instead of refusing to
684 * startup the whole VirtualBox server in case if the settings file of some
685 * registered VM is invalid or inaccessible.
686 *
687 * @note Must be always called from this object's write lock
688 * (unless called from #init() that doesn't need any locking).
689 * @note Locks the mUSBController method for writing.
690 * @note Subclasses must not call this method.
691 */
692HRESULT Machine::registeredInit()
693{
694 AssertReturn(!isSessionMachine(), E_FAIL);
695 AssertReturn(!isSnapshotMachine(), E_FAIL);
696 AssertReturn(mData->mUuid.isValid(), E_FAIL);
697 AssertReturn(!mData->mAccessible, E_FAIL);
698
699 HRESULT rc = initDataAndChildObjects();
700
701 if (SUCCEEDED(rc))
702 {
703 /* Temporarily reset the registered flag in order to let setters
704 * potentially called from loadSettings() succeed (isMutable() used in
705 * all setters will return FALSE for a Machine instance if mRegistered
706 * is TRUE). */
707 mData->mRegistered = FALSE;
708
709 try
710 {
711 // load and parse machine XML; this will throw on XML or logic errors
712 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
713
714 if (mData->mUuid != mData->pMachineConfigFile->uuid)
715 throw setError(E_FAIL,
716 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
717 mData->pMachineConfigFile->uuid.raw(),
718 mData->m_strConfigFileFull.c_str(),
719 mData->mUuid.toString().c_str(),
720 mParent->settingsFilePath().c_str());
721
722 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
723 NULL /* const Guid *puuidRegistry */);
724 if (FAILED(rc)) throw rc;
725 }
726 catch (HRESULT err)
727 {
728 /* we assume that error info is set by the thrower */
729 rc = err;
730 }
731 catch (...)
732 {
733 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
734 }
735
736 /* Restore the registered flag (even on failure) */
737 mData->mRegistered = TRUE;
738 }
739
740 if (SUCCEEDED(rc))
741 {
742 /* Set mAccessible to TRUE only if we successfully locked and loaded
743 * the settings file */
744 mData->mAccessible = TRUE;
745
746 /* commit all changes made during loading the settings file */
747 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
748 /// @todo r=klaus for some reason the settings loading logic backs up
749 // the settings, and therefore a commit is needed. Should probably be changed.
750 }
751 else
752 {
753 /* If the machine is registered, then, instead of returning a
754 * failure, we mark it as inaccessible and set the result to
755 * success to give it a try later */
756
757 /* fetch the current error info */
758 mData->mAccessError = com::ErrorInfo();
759 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
760 mData->mUuid.raw(),
761 mData->mAccessError.getText().raw()));
762
763 /* rollback all changes */
764 rollback(false /* aNotify */);
765
766 // uninit media from this machine's media registry, or else
767 // reloading the settings will fail
768 mParent->unregisterMachineMedia(getId());
769
770 /* uninitialize the common part to make sure all data is reset to
771 * default (null) values */
772 uninitDataAndChildObjects();
773
774 rc = S_OK;
775 }
776
777 return rc;
778}
779
780/**
781 * Uninitializes the instance.
782 * Called either from FinalRelease() or by the parent when it gets destroyed.
783 *
784 * @note The caller of this method must make sure that this object
785 * a) doesn't have active callers on the current thread and b) is not locked
786 * by the current thread; otherwise uninit() will hang either a) due to
787 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
788 * a dead-lock caused by this thread waiting for all callers on the other
789 * threads are done but preventing them from doing so by holding a lock.
790 */
791void Machine::uninit()
792{
793 LogFlowThisFuncEnter();
794
795 Assert(!isWriteLockOnCurrentThread());
796
797 Assert(!uRegistryNeedsSaving);
798 if (uRegistryNeedsSaving)
799 {
800 AutoCaller autoCaller(this);
801 if (SUCCEEDED(autoCaller.rc()))
802 {
803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
804 saveSettings(NULL, Machine::SaveS_Force);
805 }
806 }
807
808 /* Enclose the state transition Ready->InUninit->NotReady */
809 AutoUninitSpan autoUninitSpan(this);
810 if (autoUninitSpan.uninitDone())
811 return;
812
813 Assert(!isSnapshotMachine());
814 Assert(!isSessionMachine());
815 Assert(!!mData);
816
817 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
818 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
819
820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
821
822 if (!mData->mSession.mMachine.isNull())
823 {
824 /* Theoretically, this can only happen if the VirtualBox server has been
825 * terminated while there were clients running that owned open direct
826 * sessions. Since in this case we are definitely called by
827 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
828 * won't happen on the client watcher thread (because it does
829 * VirtualBox::addCaller() for the duration of the
830 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
831 * cannot happen until the VirtualBox caller is released). This is
832 * important, because SessionMachine::uninit() cannot correctly operate
833 * after we return from this method (it expects the Machine instance is
834 * still valid). We'll call it ourselves below.
835 */
836 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
837 (SessionMachine*)mData->mSession.mMachine));
838
839 if (Global::IsOnlineOrTransient(mData->mMachineState))
840 {
841 LogWarningThisFunc(("Setting state to Aborted!\n"));
842 /* set machine state using SessionMachine reimplementation */
843 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
844 }
845
846 /*
847 * Uninitialize SessionMachine using public uninit() to indicate
848 * an unexpected uninitialization.
849 */
850 mData->mSession.mMachine->uninit();
851 /* SessionMachine::uninit() must set mSession.mMachine to null */
852 Assert(mData->mSession.mMachine.isNull());
853 }
854
855 // uninit media from this machine's media registry, if they're still there
856 Guid uuidMachine(getId());
857
858 /* the lock is no more necessary (SessionMachine is uninitialized) */
859 alock.release();
860
861 /* XXX This will fail with
862 * "cannot be closed because it is still attached to 1 virtual machines"
863 * because at this point we did not call uninitDataAndChildObjects() yet
864 * and therefore also removeBackReference() for all these mediums was not called! */
865
866 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
867 mParent->unregisterMachineMedia(uuidMachine);
868
869 // has machine been modified?
870 if (mData->flModifications)
871 {
872 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
873 rollback(false /* aNotify */);
874 }
875
876 if (mData->mAccessible)
877 uninitDataAndChildObjects();
878
879 /* free the essential data structure last */
880 mData.free();
881
882 LogFlowThisFuncLeave();
883}
884
885// IMachine properties
886/////////////////////////////////////////////////////////////////////////////
887
888STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
889{
890 CheckComArgOutPointerValid(aParent);
891
892 AutoLimitedCaller autoCaller(this);
893 if (FAILED(autoCaller.rc())) return autoCaller.rc();
894
895 /* mParent is constant during life time, no need to lock */
896 ComObjPtr<VirtualBox> pVirtualBox(mParent);
897 pVirtualBox.queryInterfaceTo(aParent);
898
899 return S_OK;
900}
901
902STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
903{
904 CheckComArgOutPointerValid(aAccessible);
905
906 AutoLimitedCaller autoCaller(this);
907 if (FAILED(autoCaller.rc())) return autoCaller.rc();
908
909 LogFlowThisFunc(("ENTER\n"));
910
911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
912
913 HRESULT rc = S_OK;
914
915 if (!mData->mAccessible)
916 {
917 /* try to initialize the VM once more if not accessible */
918
919 AutoReinitSpan autoReinitSpan(this);
920 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
921
922#ifdef DEBUG
923 LogFlowThisFunc(("Dumping media backreferences\n"));
924 mParent->dumpAllBackRefs();
925#endif
926
927 if (mData->pMachineConfigFile)
928 {
929 // reset the XML file to force loadSettings() (called from registeredInit())
930 // to parse it again; the file might have changed
931 delete mData->pMachineConfigFile;
932 mData->pMachineConfigFile = NULL;
933 }
934
935 rc = registeredInit();
936
937 if (SUCCEEDED(rc) && mData->mAccessible)
938 {
939 autoReinitSpan.setSucceeded();
940
941 /* make sure interesting parties will notice the accessibility
942 * state change */
943 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
944 mParent->onMachineDataChange(mData->mUuid);
945 }
946 }
947
948 if (SUCCEEDED(rc))
949 *aAccessible = mData->mAccessible;
950
951 LogFlowThisFuncLeave();
952
953 return rc;
954}
955
956STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
957{
958 CheckComArgOutPointerValid(aAccessError);
959
960 AutoLimitedCaller autoCaller(this);
961 if (FAILED(autoCaller.rc())) return autoCaller.rc();
962
963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
964
965 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
966 {
967 /* return shortly */
968 aAccessError = NULL;
969 return S_OK;
970 }
971
972 HRESULT rc = S_OK;
973
974 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
975 rc = errorInfo.createObject();
976 if (SUCCEEDED(rc))
977 {
978 errorInfo->init(mData->mAccessError.getResultCode(),
979 mData->mAccessError.getInterfaceID().ref(),
980 Utf8Str(mData->mAccessError.getComponent()).c_str(),
981 Utf8Str(mData->mAccessError.getText()));
982 rc = errorInfo.queryInterfaceTo(aAccessError);
983 }
984
985 return rc;
986}
987
988STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
989{
990 CheckComArgOutPointerValid(aName);
991
992 AutoCaller autoCaller(this);
993 if (FAILED(autoCaller.rc())) return autoCaller.rc();
994
995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 mUserData->s.strName.cloneTo(aName);
998
999 return S_OK;
1000}
1001
1002STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1003{
1004 CheckComArgStrNotEmptyOrNull(aName);
1005
1006 AutoCaller autoCaller(this);
1007 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1008
1009 // prohibit setting a UUID only as the machine name, or else it can
1010 // never be found by findMachine()
1011 Guid test(aName);
1012
1013 if (test.isValid())
1014 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1015
1016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1017
1018 HRESULT rc = checkStateDependency(MutableStateDep);
1019 if (FAILED(rc)) return rc;
1020
1021 setModified(IsModified_MachineData);
1022 mUserData.backup();
1023 mUserData->s.strName = aName;
1024
1025 return S_OK;
1026}
1027
1028STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1029{
1030 CheckComArgOutPointerValid(aDescription);
1031
1032 AutoCaller autoCaller(this);
1033 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1034
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 mUserData->s.strDescription.cloneTo(aDescription);
1038
1039 return S_OK;
1040}
1041
1042STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1043{
1044 AutoCaller autoCaller(this);
1045 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1046
1047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1048
1049 // this can be done in principle in any state as it doesn't affect the VM
1050 // significantly, but play safe by not messing around while complex
1051 // activities are going on
1052 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1053 if (FAILED(rc)) return rc;
1054
1055 setModified(IsModified_MachineData);
1056 mUserData.backup();
1057 mUserData->s.strDescription = aDescription;
1058
1059 return S_OK;
1060}
1061
1062STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1063{
1064 CheckComArgOutPointerValid(aId);
1065
1066 AutoLimitedCaller autoCaller(this);
1067 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1068
1069 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1070
1071 mData->mUuid.toUtf16().cloneTo(aId);
1072
1073 return S_OK;
1074}
1075
1076STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1077{
1078 CheckComArgOutSafeArrayPointerValid(aGroups);
1079
1080 AutoCaller autoCaller(this);
1081 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1082
1083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1084 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1085 size_t i = 0;
1086 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1087 it != mUserData->s.llGroups.end();
1088 ++it, i++)
1089 {
1090 Bstr tmp = *it;
1091 tmp.cloneTo(&groups[i]);
1092 }
1093 groups.detachTo(ComSafeArrayOutArg(aGroups));
1094
1095 return S_OK;
1096}
1097
1098STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1099{
1100 AutoCaller autoCaller(this);
1101 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1102
1103 StringsList llGroups;
1104 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1105 if (FAILED(rc))
1106 return rc;
1107
1108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 // changing machine groups is possible while the VM is offline
1111 rc = checkStateDependency(OfflineStateDep);
1112 if (FAILED(rc)) return rc;
1113
1114 setModified(IsModified_MachineData);
1115 mUserData.backup();
1116 mUserData->s.llGroups = llGroups;
1117
1118 return S_OK;
1119}
1120
1121STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1122{
1123 CheckComArgOutPointerValid(aOSTypeId);
1124
1125 AutoCaller autoCaller(this);
1126 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1127
1128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1129
1130 mUserData->s.strOsType.cloneTo(aOSTypeId);
1131
1132 return S_OK;
1133}
1134
1135STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1136{
1137 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1138
1139 AutoCaller autoCaller(this);
1140 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1141
1142 /* look up the object by Id to check it is valid */
1143 ComPtr<IGuestOSType> guestOSType;
1144 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1145 if (FAILED(rc)) return rc;
1146
1147 /* when setting, always use the "etalon" value for consistency -- lookup
1148 * by ID is case-insensitive and the input value may have different case */
1149 Bstr osTypeId;
1150 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1151 if (FAILED(rc)) return rc;
1152
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 rc = checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 setModified(IsModified_MachineData);
1159 mUserData.backup();
1160 mUserData->s.strOsType = osTypeId;
1161
1162 return S_OK;
1163}
1164
1165
1166STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1167{
1168 CheckComArgOutPointerValid(aFirmwareType);
1169
1170 AutoCaller autoCaller(this);
1171 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1172
1173 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1174
1175 *aFirmwareType = mHWData->mFirmwareType;
1176
1177 return S_OK;
1178}
1179
1180STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1181{
1182 AutoCaller autoCaller(this);
1183 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1185
1186 HRESULT rc = checkStateDependency(MutableStateDep);
1187 if (FAILED(rc)) return rc;
1188
1189 setModified(IsModified_MachineData);
1190 mHWData.backup();
1191 mHWData->mFirmwareType = aFirmwareType;
1192
1193 return S_OK;
1194}
1195
1196STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1197{
1198 CheckComArgOutPointerValid(aKeyboardHIDType);
1199
1200 AutoCaller autoCaller(this);
1201 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1202
1203 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1204
1205 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1206
1207 return S_OK;
1208}
1209
1210STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1211{
1212 AutoCaller autoCaller(this);
1213 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1215
1216 HRESULT rc = checkStateDependency(MutableStateDep);
1217 if (FAILED(rc)) return rc;
1218
1219 setModified(IsModified_MachineData);
1220 mHWData.backup();
1221 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1222
1223 return S_OK;
1224}
1225
1226STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1227{
1228 CheckComArgOutPointerValid(aPointingHIDType);
1229
1230 AutoCaller autoCaller(this);
1231 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1232
1233 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1234
1235 *aPointingHIDType = mHWData->mPointingHIDType;
1236
1237 return S_OK;
1238}
1239
1240STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1241{
1242 AutoCaller autoCaller(this);
1243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1244 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1245
1246 HRESULT rc = checkStateDependency(MutableStateDep);
1247 if (FAILED(rc)) return rc;
1248
1249 setModified(IsModified_MachineData);
1250 mHWData.backup();
1251 mHWData->mPointingHIDType = aPointingHIDType;
1252
1253 return S_OK;
1254}
1255
1256STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1257{
1258 CheckComArgOutPointerValid(aChipsetType);
1259
1260 AutoCaller autoCaller(this);
1261 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1262
1263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1264
1265 *aChipsetType = mHWData->mChipsetType;
1266
1267 return S_OK;
1268}
1269
1270STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1271{
1272 AutoCaller autoCaller(this);
1273 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 HRESULT rc = checkStateDependency(MutableStateDep);
1277 if (FAILED(rc)) return rc;
1278
1279 if (aChipsetType != mHWData->mChipsetType)
1280 {
1281 setModified(IsModified_MachineData);
1282 mHWData.backup();
1283 mHWData->mChipsetType = aChipsetType;
1284
1285 // Resize network adapter array, to be finalized on commit/rollback.
1286 // We must not throw away entries yet, otherwise settings are lost
1287 // without a way to roll back.
1288 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1289 uint32_t oldCount = mNetworkAdapters.size();
1290 if (newCount > oldCount)
1291 {
1292 mNetworkAdapters.resize(newCount);
1293 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1294 {
1295 unconst(mNetworkAdapters[slot]).createObject();
1296 mNetworkAdapters[slot]->init(this, slot);
1297 }
1298 }
1299 }
1300
1301 return S_OK;
1302}
1303
1304STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1305{
1306 CheckComArgOutPointerValid(aHWVersion);
1307
1308 AutoCaller autoCaller(this);
1309 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1310
1311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1312
1313 mHWData->mHWVersion.cloneTo(aHWVersion);
1314
1315 return S_OK;
1316}
1317
1318STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1319{
1320 /* check known version */
1321 Utf8Str hwVersion = aHWVersion;
1322 if ( hwVersion.compare("1") != 0
1323 && hwVersion.compare("2") != 0)
1324 return setError(E_INVALIDARG,
1325 tr("Invalid hardware version: %ls\n"), aHWVersion);
1326
1327 AutoCaller autoCaller(this);
1328 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1329
1330 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1331
1332 HRESULT rc = checkStateDependency(MutableStateDep);
1333 if (FAILED(rc)) return rc;
1334
1335 setModified(IsModified_MachineData);
1336 mHWData.backup();
1337 mHWData->mHWVersion = hwVersion;
1338
1339 return S_OK;
1340}
1341
1342STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1343{
1344 CheckComArgOutPointerValid(aUUID);
1345
1346 AutoCaller autoCaller(this);
1347 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1348
1349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1350
1351 if (mHWData->mHardwareUUID.isValid())
1352 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1353 else
1354 mData->mUuid.toUtf16().cloneTo(aUUID);
1355
1356 return S_OK;
1357}
1358
1359STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1360{
1361 Guid hardwareUUID(aUUID);
1362 if (!hardwareUUID.isValid())
1363 return E_INVALIDARG;
1364
1365 AutoCaller autoCaller(this);
1366 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1367
1368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1369
1370 HRESULT rc = checkStateDependency(MutableStateDep);
1371 if (FAILED(rc)) return rc;
1372
1373 setModified(IsModified_MachineData);
1374 mHWData.backup();
1375 if (hardwareUUID == mData->mUuid)
1376 mHWData->mHardwareUUID.clear();
1377 else
1378 mHWData->mHardwareUUID = hardwareUUID;
1379
1380 return S_OK;
1381}
1382
1383STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1384{
1385 CheckComArgOutPointerValid(memorySize);
1386
1387 AutoCaller autoCaller(this);
1388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1389
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 *memorySize = mHWData->mMemorySize;
1393
1394 return S_OK;
1395}
1396
1397STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1398{
1399 /* check RAM limits */
1400 if ( memorySize < MM_RAM_MIN_IN_MB
1401 || memorySize > MM_RAM_MAX_IN_MB
1402 )
1403 return setError(E_INVALIDARG,
1404 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1405 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1406
1407 AutoCaller autoCaller(this);
1408 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1409
1410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1411
1412 HRESULT rc = checkStateDependency(MutableStateDep);
1413 if (FAILED(rc)) return rc;
1414
1415 setModified(IsModified_MachineData);
1416 mHWData.backup();
1417 mHWData->mMemorySize = memorySize;
1418
1419 return S_OK;
1420}
1421
1422STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1423{
1424 CheckComArgOutPointerValid(CPUCount);
1425
1426 AutoCaller autoCaller(this);
1427 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1428
1429 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1430
1431 *CPUCount = mHWData->mCPUCount;
1432
1433 return S_OK;
1434}
1435
1436STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1437{
1438 /* check CPU limits */
1439 if ( CPUCount < SchemaDefs::MinCPUCount
1440 || CPUCount > SchemaDefs::MaxCPUCount
1441 )
1442 return setError(E_INVALIDARG,
1443 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1444 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1445
1446 AutoCaller autoCaller(this);
1447 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1448
1449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1450
1451 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1452 if (mHWData->mCPUHotPlugEnabled)
1453 {
1454 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1455 {
1456 if (mHWData->mCPUAttached[idx])
1457 return setError(E_INVALIDARG,
1458 tr("There is still a CPU attached to socket %lu."
1459 "Detach the CPU before removing the socket"),
1460 CPUCount, idx+1);
1461 }
1462 }
1463
1464 HRESULT rc = checkStateDependency(MutableStateDep);
1465 if (FAILED(rc)) return rc;
1466
1467 setModified(IsModified_MachineData);
1468 mHWData.backup();
1469 mHWData->mCPUCount = CPUCount;
1470
1471 return S_OK;
1472}
1473
1474STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1475{
1476 CheckComArgOutPointerValid(aExecutionCap);
1477
1478 AutoCaller autoCaller(this);
1479 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1480
1481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1482
1483 *aExecutionCap = mHWData->mCpuExecutionCap;
1484
1485 return S_OK;
1486}
1487
1488STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1489{
1490 HRESULT rc = S_OK;
1491
1492 /* check throttle limits */
1493 if ( aExecutionCap < 1
1494 || aExecutionCap > 100
1495 )
1496 return setError(E_INVALIDARG,
1497 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1498 aExecutionCap, 1, 100);
1499
1500 AutoCaller autoCaller(this);
1501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1502
1503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1504
1505 alock.release();
1506 rc = onCPUExecutionCapChange(aExecutionCap);
1507 alock.acquire();
1508 if (FAILED(rc)) return rc;
1509
1510 setModified(IsModified_MachineData);
1511 mHWData.backup();
1512 mHWData->mCpuExecutionCap = aExecutionCap;
1513
1514 /* Save settings if online - todo why is this required?? */
1515 if (Global::IsOnline(mData->mMachineState))
1516 saveSettings(NULL);
1517
1518 return S_OK;
1519}
1520
1521
1522STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1523{
1524 CheckComArgOutPointerValid(enabled);
1525
1526 AutoCaller autoCaller(this);
1527 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1528
1529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1530
1531 *enabled = mHWData->mCPUHotPlugEnabled;
1532
1533 return S_OK;
1534}
1535
1536STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1537{
1538 HRESULT rc = S_OK;
1539
1540 AutoCaller autoCaller(this);
1541 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1542
1543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1544
1545 rc = checkStateDependency(MutableStateDep);
1546 if (FAILED(rc)) return rc;
1547
1548 if (mHWData->mCPUHotPlugEnabled != enabled)
1549 {
1550 if (enabled)
1551 {
1552 setModified(IsModified_MachineData);
1553 mHWData.backup();
1554
1555 /* Add the amount of CPUs currently attached */
1556 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1557 {
1558 mHWData->mCPUAttached[i] = true;
1559 }
1560 }
1561 else
1562 {
1563 /*
1564 * We can disable hotplug only if the amount of maximum CPUs is equal
1565 * to the amount of attached CPUs
1566 */
1567 unsigned cCpusAttached = 0;
1568 unsigned iHighestId = 0;
1569
1570 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1571 {
1572 if (mHWData->mCPUAttached[i])
1573 {
1574 cCpusAttached++;
1575 iHighestId = i;
1576 }
1577 }
1578
1579 if ( (cCpusAttached != mHWData->mCPUCount)
1580 || (iHighestId >= mHWData->mCPUCount))
1581 return setError(E_INVALIDARG,
1582 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1583
1584 setModified(IsModified_MachineData);
1585 mHWData.backup();
1586 }
1587 }
1588
1589 mHWData->mCPUHotPlugEnabled = enabled;
1590
1591 return rc;
1592}
1593
1594STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1595{
1596#ifdef VBOX_WITH_USB_CARDREADER
1597 CheckComArgOutPointerValid(enabled);
1598
1599 AutoCaller autoCaller(this);
1600 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1601
1602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1603
1604 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1605
1606 return S_OK;
1607#else
1608 NOREF(enabled);
1609 return E_NOTIMPL;
1610#endif
1611}
1612
1613STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1614{
1615#ifdef VBOX_WITH_USB_CARDREADER
1616 AutoCaller autoCaller(this);
1617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1618 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1619
1620 HRESULT rc = checkStateDependency(MutableStateDep);
1621 if (FAILED(rc)) return rc;
1622
1623 setModified(IsModified_MachineData);
1624 mHWData.backup();
1625 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1626
1627 return S_OK;
1628#else
1629 NOREF(enabled);
1630 return E_NOTIMPL;
1631#endif
1632}
1633
1634STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1635{
1636#ifdef VBOX_WITH_USB_VIDEO
1637 CheckComArgOutPointerValid(enabled);
1638
1639 AutoCaller autoCaller(this);
1640 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1641
1642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1643
1644 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1645
1646 return S_OK;
1647#else
1648 NOREF(enabled);
1649 return E_NOTIMPL;
1650#endif
1651}
1652
1653STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1654{
1655#ifdef VBOX_WITH_USB_VIDEO
1656 AutoCaller autoCaller(this);
1657 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659
1660 HRESULT rc = checkStateDependency(MutableStateDep);
1661 if (FAILED(rc)) return rc;
1662
1663 setModified(IsModified_MachineData);
1664 mHWData.backup();
1665 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1666
1667 return S_OK;
1668#else
1669 NOREF(enabled);
1670 return E_NOTIMPL;
1671#endif
1672}
1673
1674STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1675{
1676 CheckComArgOutPointerValid(enabled);
1677
1678 AutoCaller autoCaller(this);
1679 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1681
1682 *enabled = mHWData->mHPETEnabled;
1683
1684 return S_OK;
1685}
1686
1687STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1688{
1689 HRESULT rc = S_OK;
1690
1691 AutoCaller autoCaller(this);
1692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 rc = checkStateDependency(MutableStateDep);
1696 if (FAILED(rc)) return rc;
1697
1698 setModified(IsModified_MachineData);
1699 mHWData.backup();
1700
1701 mHWData->mHPETEnabled = enabled;
1702
1703 return rc;
1704}
1705
1706STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1707{
1708 AutoCaller autoCaller(this);
1709 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1710
1711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1712
1713 *fEnabled = mHWData->mVideoCaptureEnabled;
1714 return S_OK;
1715}
1716
1717STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1718{
1719 AutoCaller autoCaller(this);
1720 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1721
1722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1723 mHWData->mVideoCaptureEnabled = fEnabled;
1724 return S_OK;
1725}
1726
1727STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1728{
1729 AutoCaller autoCaller(this);
1730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1731
1732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1733 mHWData->mVideoCaptureFile.cloneTo(apFile);
1734 return S_OK;
1735}
1736
1737STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1738{
1739 Utf8Str strFile(aFile);
1740 AutoCaller autoCaller(this);
1741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1742
1743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1744 if (strFile.isEmpty())
1745 strFile = "VideoCap.webm";
1746 mHWData->mVideoCaptureFile = strFile;
1747 return S_OK;
1748}
1749
1750
1751STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1752{
1753 AutoCaller autoCaller(this);
1754 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1755
1756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1757 *ulHorzRes = mHWData->mVideoCaptureWidth;
1758 return S_OK;
1759}
1760
1761STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1762{
1763 AutoCaller autoCaller(this);
1764 if (FAILED(autoCaller.rc()))
1765 {
1766 LogFlow(("Autolocked failed\n"));
1767 return autoCaller.rc();
1768 }
1769
1770 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1771 mHWData->mVideoCaptureWidth = ulHorzRes;
1772 return S_OK;
1773}
1774
1775STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1776{
1777 AutoCaller autoCaller(this);
1778 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1779
1780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1781 *ulVertRes = mHWData->mVideoCaptureHeight;
1782 return S_OK;
1783}
1784
1785STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1786{
1787 AutoCaller autoCaller(this);
1788 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1789
1790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1791 mHWData->mVideoCaptureHeight = ulVertRes;
1792 return S_OK;
1793}
1794
1795STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1796{
1797 CheckComArgOutPointerValid(memorySize);
1798
1799 AutoCaller autoCaller(this);
1800 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1801
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803
1804 *memorySize = mHWData->mVRAMSize;
1805
1806 return S_OK;
1807}
1808
1809STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1810{
1811 /* check VRAM limits */
1812 if (memorySize < SchemaDefs::MinGuestVRAM ||
1813 memorySize > SchemaDefs::MaxGuestVRAM)
1814 return setError(E_INVALIDARG,
1815 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1816 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1817
1818 AutoCaller autoCaller(this);
1819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1820
1821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 HRESULT rc = checkStateDependency(MutableStateDep);
1824 if (FAILED(rc)) return rc;
1825
1826 setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mVRAMSize = memorySize;
1829
1830 return S_OK;
1831}
1832
1833/** @todo this method should not be public */
1834STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1835{
1836 CheckComArgOutPointerValid(memoryBalloonSize);
1837
1838 AutoCaller autoCaller(this);
1839 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1840
1841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1842
1843 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1844
1845 return S_OK;
1846}
1847
1848/**
1849 * Set the memory balloon size.
1850 *
1851 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1852 * we have to make sure that we never call IGuest from here.
1853 */
1854STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1855{
1856 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1857#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1858 /* check limits */
1859 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1860 return setError(E_INVALIDARG,
1861 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1862 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1863
1864 AutoCaller autoCaller(this);
1865 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1866
1867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1868
1869 setModified(IsModified_MachineData);
1870 mHWData.backup();
1871 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1872
1873 return S_OK;
1874#else
1875 NOREF(memoryBalloonSize);
1876 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1877#endif
1878}
1879
1880STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1881{
1882 CheckComArgOutPointerValid(enabled);
1883
1884 AutoCaller autoCaller(this);
1885 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1886
1887 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 *enabled = mHWData->mPageFusionEnabled;
1890 return S_OK;
1891}
1892
1893STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1894{
1895#ifdef VBOX_WITH_PAGE_SHARING
1896 AutoCaller autoCaller(this);
1897 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1898
1899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1900
1901 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1902 setModified(IsModified_MachineData);
1903 mHWData.backup();
1904 mHWData->mPageFusionEnabled = enabled;
1905 return S_OK;
1906#else
1907 NOREF(enabled);
1908 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1909#endif
1910}
1911
1912STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1913{
1914 CheckComArgOutPointerValid(enabled);
1915
1916 AutoCaller autoCaller(this);
1917 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1918
1919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1920
1921 *enabled = mHWData->mAccelerate3DEnabled;
1922
1923 return S_OK;
1924}
1925
1926STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1927{
1928 AutoCaller autoCaller(this);
1929 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1930
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 HRESULT rc = checkStateDependency(MutableStateDep);
1934 if (FAILED(rc)) return rc;
1935
1936 /** @todo check validity! */
1937
1938 setModified(IsModified_MachineData);
1939 mHWData.backup();
1940 mHWData->mAccelerate3DEnabled = enable;
1941
1942 return S_OK;
1943}
1944
1945
1946STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1947{
1948 CheckComArgOutPointerValid(enabled);
1949
1950 AutoCaller autoCaller(this);
1951 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1952
1953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 *enabled = mHWData->mAccelerate2DVideoEnabled;
1956
1957 return S_OK;
1958}
1959
1960STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
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 /** @todo check validity! */
1971
1972 setModified(IsModified_MachineData);
1973 mHWData.backup();
1974 mHWData->mAccelerate2DVideoEnabled = enable;
1975
1976 return S_OK;
1977}
1978
1979STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
1980{
1981 CheckComArgOutPointerValid(monitorCount);
1982
1983 AutoCaller autoCaller(this);
1984 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1985
1986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1987
1988 *monitorCount = mHWData->mMonitorCount;
1989
1990 return S_OK;
1991}
1992
1993STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
1994{
1995 /* make sure monitor count is a sensible number */
1996 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
1997 return setError(E_INVALIDARG,
1998 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1999 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2000
2001 AutoCaller autoCaller(this);
2002 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2003
2004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 HRESULT rc = checkStateDependency(MutableStateDep);
2007 if (FAILED(rc)) return rc;
2008
2009 setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mMonitorCount = monitorCount;
2012
2013 return S_OK;
2014}
2015
2016STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2017{
2018 CheckComArgOutPointerValid(biosSettings);
2019
2020 AutoCaller autoCaller(this);
2021 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2022
2023 /* mBIOSSettings is constant during life time, no need to lock */
2024 mBIOSSettings.queryInterfaceTo(biosSettings);
2025
2026 return S_OK;
2027}
2028
2029STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2030{
2031 CheckComArgOutPointerValid(aVal);
2032
2033 AutoCaller autoCaller(this);
2034 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2035
2036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2037
2038 switch (property)
2039 {
2040 case CPUPropertyType_PAE:
2041 *aVal = mHWData->mPAEEnabled;
2042 break;
2043
2044 case CPUPropertyType_Synthetic:
2045 *aVal = mHWData->mSyntheticCpu;
2046 break;
2047
2048 case CPUPropertyType_LongMode:
2049 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2050 *aVal = TRUE;
2051 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2052 *aVal = FALSE;
2053#if HC_ARCH_BITS == 64
2054 else
2055 *aVal = TRUE;
2056#else
2057 else
2058 {
2059 *aVal = FALSE;
2060
2061 ComPtr<IGuestOSType> guestOSType;
2062 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), guestOSType.asOutParam());
2063 if (SUCCEEDED(hrc2))
2064 {
2065 BOOL fIs64Bit = FALSE;
2066 hrc2 = pGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc);
2067 if (SUCCEEDED(hrc2) && fIs64Bit)
2068 {
2069 ComObjPtr<Host> ptrHost = mParent->host();
2070 alock.release();
2071 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal)
2072 AssertComRC(hrc);
2073 if (FAILURE(hrc2))
2074 *aVal = FALSE;
2075 }
2076 }
2077#endif
2078 break;
2079
2080 default:
2081 return E_INVALIDARG;
2082 }
2083 return S_OK;
2084}
2085
2086STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2087{
2088 AutoCaller autoCaller(this);
2089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2090
2091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2092
2093 HRESULT rc = checkStateDependency(MutableStateDep);
2094 if (FAILED(rc)) return rc;
2095
2096 switch (property)
2097 {
2098 case CPUPropertyType_PAE:
2099 setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mPAEEnabled = !!aVal;
2102 break;
2103
2104 case CPUPropertyType_Synthetic:
2105 setModified(IsModified_MachineData);
2106 mHWData.backup();
2107 mHWData->mSyntheticCpu = !!aVal;
2108 break;
2109
2110 case CPUPropertyType_LongMode:
2111 setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2114 break;
2115
2116 default:
2117 return E_INVALIDARG;
2118 }
2119 return S_OK;
2120}
2121
2122STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2123{
2124 CheckComArgOutPointerValid(aValEax);
2125 CheckComArgOutPointerValid(aValEbx);
2126 CheckComArgOutPointerValid(aValEcx);
2127 CheckComArgOutPointerValid(aValEdx);
2128
2129 AutoCaller autoCaller(this);
2130 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2131
2132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2133
2134 switch(aId)
2135 {
2136 case 0x0:
2137 case 0x1:
2138 case 0x2:
2139 case 0x3:
2140 case 0x4:
2141 case 0x5:
2142 case 0x6:
2143 case 0x7:
2144 case 0x8:
2145 case 0x9:
2146 case 0xA:
2147 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2148 return E_INVALIDARG;
2149
2150 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2151 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2152 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2153 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2154 break;
2155
2156 case 0x80000000:
2157 case 0x80000001:
2158 case 0x80000002:
2159 case 0x80000003:
2160 case 0x80000004:
2161 case 0x80000005:
2162 case 0x80000006:
2163 case 0x80000007:
2164 case 0x80000008:
2165 case 0x80000009:
2166 case 0x8000000A:
2167 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2168 return E_INVALIDARG;
2169
2170 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2171 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2172 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2173 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2174 break;
2175
2176 default:
2177 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2178 }
2179 return S_OK;
2180}
2181
2182STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2183{
2184 AutoCaller autoCaller(this);
2185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2186
2187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2188
2189 HRESULT rc = checkStateDependency(MutableStateDep);
2190 if (FAILED(rc)) return rc;
2191
2192 switch(aId)
2193 {
2194 case 0x0:
2195 case 0x1:
2196 case 0x2:
2197 case 0x3:
2198 case 0x4:
2199 case 0x5:
2200 case 0x6:
2201 case 0x7:
2202 case 0x8:
2203 case 0x9:
2204 case 0xA:
2205 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2206 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2207 setModified(IsModified_MachineData);
2208 mHWData.backup();
2209 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2210 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2211 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2212 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2213 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2214 break;
2215
2216 case 0x80000000:
2217 case 0x80000001:
2218 case 0x80000002:
2219 case 0x80000003:
2220 case 0x80000004:
2221 case 0x80000005:
2222 case 0x80000006:
2223 case 0x80000007:
2224 case 0x80000008:
2225 case 0x80000009:
2226 case 0x8000000A:
2227 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2228 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2229 setModified(IsModified_MachineData);
2230 mHWData.backup();
2231 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2232 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2233 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2234 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2235 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2236 break;
2237
2238 default:
2239 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2240 }
2241 return S_OK;
2242}
2243
2244STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2245{
2246 AutoCaller autoCaller(this);
2247 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2248
2249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2250
2251 HRESULT rc = checkStateDependency(MutableStateDep);
2252 if (FAILED(rc)) return rc;
2253
2254 switch(aId)
2255 {
2256 case 0x0:
2257 case 0x1:
2258 case 0x2:
2259 case 0x3:
2260 case 0x4:
2261 case 0x5:
2262 case 0x6:
2263 case 0x7:
2264 case 0x8:
2265 case 0x9:
2266 case 0xA:
2267 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2268 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2269 setModified(IsModified_MachineData);
2270 mHWData.backup();
2271 /* Invalidate leaf. */
2272 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2273 break;
2274
2275 case 0x80000000:
2276 case 0x80000001:
2277 case 0x80000002:
2278 case 0x80000003:
2279 case 0x80000004:
2280 case 0x80000005:
2281 case 0x80000006:
2282 case 0x80000007:
2283 case 0x80000008:
2284 case 0x80000009:
2285 case 0x8000000A:
2286 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2287 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2288 setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 /* Invalidate leaf. */
2291 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2292 break;
2293
2294 default:
2295 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2296 }
2297 return S_OK;
2298}
2299
2300STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2301{
2302 AutoCaller autoCaller(this);
2303 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2304
2305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2306
2307 HRESULT rc = checkStateDependency(MutableStateDep);
2308 if (FAILED(rc)) return rc;
2309
2310 setModified(IsModified_MachineData);
2311 mHWData.backup();
2312
2313 /* Invalidate all standard leafs. */
2314 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2315 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2316
2317 /* Invalidate all extended leafs. */
2318 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2319 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2320
2321 return S_OK;
2322}
2323
2324STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2325{
2326 CheckComArgOutPointerValid(aVal);
2327
2328 AutoCaller autoCaller(this);
2329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2330
2331 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2332
2333 switch(property)
2334 {
2335 case HWVirtExPropertyType_Enabled:
2336 *aVal = mHWData->mHWVirtExEnabled;
2337 break;
2338
2339 case HWVirtExPropertyType_Exclusive:
2340 *aVal = mHWData->mHWVirtExExclusive;
2341 break;
2342
2343 case HWVirtExPropertyType_VPID:
2344 *aVal = mHWData->mHWVirtExVPIDEnabled;
2345 break;
2346
2347 case HWVirtExPropertyType_NestedPaging:
2348 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2349 break;
2350
2351 case HWVirtExPropertyType_LargePages:
2352 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2353#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2354 *aVal = FALSE;
2355#endif
2356 break;
2357
2358 case HWVirtExPropertyType_Force:
2359 *aVal = mHWData->mHWVirtExForceEnabled;
2360 break;
2361
2362 default:
2363 return E_INVALIDARG;
2364 }
2365 return S_OK;
2366}
2367
2368STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2369{
2370 AutoCaller autoCaller(this);
2371 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2372
2373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2374
2375 HRESULT rc = checkStateDependency(MutableStateDep);
2376 if (FAILED(rc)) return rc;
2377
2378 switch(property)
2379 {
2380 case HWVirtExPropertyType_Enabled:
2381 setModified(IsModified_MachineData);
2382 mHWData.backup();
2383 mHWData->mHWVirtExEnabled = !!aVal;
2384 break;
2385
2386 case HWVirtExPropertyType_Exclusive:
2387 setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mHWVirtExExclusive = !!aVal;
2390 break;
2391
2392 case HWVirtExPropertyType_VPID:
2393 setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2396 break;
2397
2398 case HWVirtExPropertyType_NestedPaging:
2399 setModified(IsModified_MachineData);
2400 mHWData.backup();
2401 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2402 break;
2403
2404 case HWVirtExPropertyType_LargePages:
2405 setModified(IsModified_MachineData);
2406 mHWData.backup();
2407 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2408 break;
2409
2410 case HWVirtExPropertyType_Force:
2411 setModified(IsModified_MachineData);
2412 mHWData.backup();
2413 mHWData->mHWVirtExForceEnabled = !!aVal;
2414 break;
2415
2416 default:
2417 return E_INVALIDARG;
2418 }
2419
2420 return S_OK;
2421}
2422
2423STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2424{
2425 CheckComArgOutPointerValid(aSnapshotFolder);
2426
2427 AutoCaller autoCaller(this);
2428 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2429
2430 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2431
2432 Utf8Str strFullSnapshotFolder;
2433 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2434 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2435
2436 return S_OK;
2437}
2438
2439STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2440{
2441 /* @todo (r=dmik):
2442 * 1. Allow to change the name of the snapshot folder containing snapshots
2443 * 2. Rename the folder on disk instead of just changing the property
2444 * value (to be smart and not to leave garbage). Note that it cannot be
2445 * done here because the change may be rolled back. Thus, the right
2446 * place is #saveSettings().
2447 */
2448
2449 AutoCaller autoCaller(this);
2450 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2451
2452 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 HRESULT rc = checkStateDependency(MutableStateDep);
2455 if (FAILED(rc)) return rc;
2456
2457 if (!mData->mCurrentSnapshot.isNull())
2458 return setError(E_FAIL,
2459 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2460
2461 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2462
2463 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2464 if (strSnapshotFolder.isEmpty())
2465 strSnapshotFolder = "Snapshots";
2466 int vrc = calculateFullPath(strSnapshotFolder,
2467 strSnapshotFolder);
2468 if (RT_FAILURE(vrc))
2469 return setError(E_FAIL,
2470 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2471 aSnapshotFolder, vrc);
2472
2473 setModified(IsModified_MachineData);
2474 mUserData.backup();
2475
2476 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2477
2478 return S_OK;
2479}
2480
2481STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2482{
2483 CheckComArgOutSafeArrayPointerValid(aAttachments);
2484
2485 AutoCaller autoCaller(this);
2486 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2487
2488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2489
2490 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2491 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2492
2493 return S_OK;
2494}
2495
2496STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2497{
2498 CheckComArgOutPointerValid(vrdeServer);
2499
2500 AutoCaller autoCaller(this);
2501 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2502
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 Assert(!!mVRDEServer);
2506 mVRDEServer.queryInterfaceTo(vrdeServer);
2507
2508 return S_OK;
2509}
2510
2511STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2512{
2513 CheckComArgOutPointerValid(audioAdapter);
2514
2515 AutoCaller autoCaller(this);
2516 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2517
2518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2519
2520 mAudioAdapter.queryInterfaceTo(audioAdapter);
2521 return S_OK;
2522}
2523
2524STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2525{
2526#ifdef VBOX_WITH_VUSB
2527 CheckComArgOutPointerValid(aUSBController);
2528
2529 AutoCaller autoCaller(this);
2530 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2531
2532 clearError();
2533 MultiResult rc(S_OK);
2534
2535# ifdef VBOX_WITH_USB
2536 rc = mParent->host()->checkUSBProxyService();
2537 if (FAILED(rc)) return rc;
2538# endif
2539
2540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2541
2542 return rc = mUSBController.queryInterfaceTo(aUSBController);
2543#else
2544 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2545 * extended error info to indicate that USB is simply not available
2546 * (w/o treating it as a failure), for example, as in OSE */
2547 NOREF(aUSBController);
2548 ReturnComNotImplemented();
2549#endif /* VBOX_WITH_VUSB */
2550}
2551
2552STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2553{
2554 CheckComArgOutPointerValid(aFilePath);
2555
2556 AutoLimitedCaller autoCaller(this);
2557 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2558
2559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 mData->m_strConfigFileFull.cloneTo(aFilePath);
2562 return S_OK;
2563}
2564
2565STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2566{
2567 CheckComArgOutPointerValid(aModified);
2568
2569 AutoCaller autoCaller(this);
2570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2571
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 HRESULT rc = checkStateDependency(MutableStateDep);
2575 if (FAILED(rc)) return rc;
2576
2577 if (!mData->pMachineConfigFile->fileExists())
2578 // this is a new machine, and no config file exists yet:
2579 *aModified = TRUE;
2580 else
2581 *aModified = (mData->flModifications != 0);
2582
2583 return S_OK;
2584}
2585
2586STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2587{
2588 CheckComArgOutPointerValid(aSessionState);
2589
2590 AutoCaller autoCaller(this);
2591 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2592
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 *aSessionState = mData->mSession.mState;
2596
2597 return S_OK;
2598}
2599
2600STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2601{
2602 CheckComArgOutPointerValid(aSessionType);
2603
2604 AutoCaller autoCaller(this);
2605 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2606
2607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2608
2609 mData->mSession.mType.cloneTo(aSessionType);
2610
2611 return S_OK;
2612}
2613
2614STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2615{
2616 CheckComArgOutPointerValid(aSessionPID);
2617
2618 AutoCaller autoCaller(this);
2619 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2620
2621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2622
2623 *aSessionPID = mData->mSession.mPID;
2624
2625 return S_OK;
2626}
2627
2628STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2629{
2630 CheckComArgOutPointerValid(machineState);
2631
2632 AutoCaller autoCaller(this);
2633 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2634
2635 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2636
2637 *machineState = mData->mMachineState;
2638
2639 return S_OK;
2640}
2641
2642STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2643{
2644 CheckComArgOutPointerValid(aLastStateChange);
2645
2646 AutoCaller autoCaller(this);
2647 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2648
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2652
2653 return S_OK;
2654}
2655
2656STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2657{
2658 CheckComArgOutPointerValid(aStateFilePath);
2659
2660 AutoCaller autoCaller(this);
2661 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2662
2663 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2664
2665 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2666
2667 return S_OK;
2668}
2669
2670STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2671{
2672 CheckComArgOutPointerValid(aLogFolder);
2673
2674 AutoCaller autoCaller(this);
2675 AssertComRCReturnRC(autoCaller.rc());
2676
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 Utf8Str logFolder;
2680 getLogFolder(logFolder);
2681 logFolder.cloneTo(aLogFolder);
2682
2683 return S_OK;
2684}
2685
2686STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2687{
2688 CheckComArgOutPointerValid(aCurrentSnapshot);
2689
2690 AutoCaller autoCaller(this);
2691 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2692
2693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2694
2695 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2696
2697 return S_OK;
2698}
2699
2700STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2701{
2702 CheckComArgOutPointerValid(aSnapshotCount);
2703
2704 AutoCaller autoCaller(this);
2705 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2706
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2710 ? 0
2711 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2712
2713 return S_OK;
2714}
2715
2716STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2717{
2718 CheckComArgOutPointerValid(aCurrentStateModified);
2719
2720 AutoCaller autoCaller(this);
2721 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2722
2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2724
2725 /* Note: for machines with no snapshots, we always return FALSE
2726 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2727 * reasons :) */
2728
2729 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2730 ? FALSE
2731 : mData->mCurrentStateModified;
2732
2733 return S_OK;
2734}
2735
2736STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2737{
2738 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2739
2740 AutoCaller autoCaller(this);
2741 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2742
2743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2744
2745 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2746 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2747
2748 return S_OK;
2749}
2750
2751STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2752{
2753 CheckComArgOutPointerValid(aClipboardMode);
2754
2755 AutoCaller autoCaller(this);
2756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2757
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 *aClipboardMode = mHWData->mClipboardMode;
2761
2762 return S_OK;
2763}
2764
2765STDMETHODIMP
2766Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2767{
2768 HRESULT rc = S_OK;
2769
2770 AutoCaller autoCaller(this);
2771 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2772
2773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2774
2775 alock.release();
2776 rc = onClipboardModeChange(aClipboardMode);
2777 alock.acquire();
2778 if (FAILED(rc)) return rc;
2779
2780 setModified(IsModified_MachineData);
2781 mHWData.backup();
2782 mHWData->mClipboardMode = aClipboardMode;
2783
2784 /* Save settings if online - todo why is this required?? */
2785 if (Global::IsOnline(mData->mMachineState))
2786 saveSettings(NULL);
2787
2788 return S_OK;
2789}
2790
2791STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2792{
2793 CheckComArgOutPointerValid(aDragAndDropMode);
2794
2795 AutoCaller autoCaller(this);
2796 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2797
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aDragAndDropMode = mHWData->mDragAndDropMode;
2801
2802 return S_OK;
2803}
2804
2805STDMETHODIMP
2806Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2807{
2808 HRESULT rc = S_OK;
2809
2810 AutoCaller autoCaller(this);
2811 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2812
2813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 alock.release();
2816 rc = onDragAndDropModeChange(aDragAndDropMode);
2817 alock.acquire();
2818 if (FAILED(rc)) return rc;
2819
2820 setModified(IsModified_MachineData);
2821 mHWData.backup();
2822 mHWData->mDragAndDropMode = aDragAndDropMode;
2823
2824 /* Save settings if online - todo why is this required?? */
2825 if (Global::IsOnline(mData->mMachineState))
2826 saveSettings(NULL);
2827
2828 return S_OK;
2829}
2830
2831STDMETHODIMP
2832Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2833{
2834 CheckComArgOutPointerValid(aPatterns);
2835
2836 AutoCaller autoCaller(this);
2837 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2838
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 try
2842 {
2843 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2844 }
2845 catch (...)
2846 {
2847 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2848 }
2849
2850 return S_OK;
2851}
2852
2853STDMETHODIMP
2854Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2855{
2856 AutoCaller autoCaller(this);
2857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2858
2859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 HRESULT rc = checkStateDependency(MutableStateDep);
2862 if (FAILED(rc)) return rc;
2863
2864 setModified(IsModified_MachineData);
2865 mHWData.backup();
2866 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2867 return rc;
2868}
2869
2870STDMETHODIMP
2871Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2872{
2873 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2874
2875 AutoCaller autoCaller(this);
2876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2877
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2881 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2882
2883 return S_OK;
2884}
2885
2886STDMETHODIMP
2887Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2888{
2889 CheckComArgOutPointerValid(aEnabled);
2890
2891 AutoCaller autoCaller(this);
2892 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2893
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 *aEnabled = mUserData->s.fTeleporterEnabled;
2897
2898 return S_OK;
2899}
2900
2901STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2902{
2903 AutoCaller autoCaller(this);
2904 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 /* Only allow it to be set to true when PoweredOff or Aborted.
2909 (Clearing it is always permitted.) */
2910 if ( aEnabled
2911 && mData->mRegistered
2912 && ( !isSessionMachine()
2913 || ( mData->mMachineState != MachineState_PoweredOff
2914 && mData->mMachineState != MachineState_Teleported
2915 && mData->mMachineState != MachineState_Aborted
2916 )
2917 )
2918 )
2919 return setError(VBOX_E_INVALID_VM_STATE,
2920 tr("The machine is not powered off (state is %s)"),
2921 Global::stringifyMachineState(mData->mMachineState));
2922
2923 setModified(IsModified_MachineData);
2924 mUserData.backup();
2925 mUserData->s.fTeleporterEnabled = !!aEnabled;
2926
2927 return S_OK;
2928}
2929
2930STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2931{
2932 CheckComArgOutPointerValid(aPort);
2933
2934 AutoCaller autoCaller(this);
2935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2936
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2940
2941 return S_OK;
2942}
2943
2944STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2945{
2946 if (aPort >= _64K)
2947 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2948
2949 AutoCaller autoCaller(this);
2950 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2951
2952 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2953
2954 HRESULT rc = checkStateDependency(MutableStateDep);
2955 if (FAILED(rc)) return rc;
2956
2957 setModified(IsModified_MachineData);
2958 mUserData.backup();
2959 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2960
2961 return S_OK;
2962}
2963
2964STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
2965{
2966 CheckComArgOutPointerValid(aAddress);
2967
2968 AutoCaller autoCaller(this);
2969 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2970
2971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2972
2973 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
2974
2975 return S_OK;
2976}
2977
2978STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
2979{
2980 AutoCaller autoCaller(this);
2981 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2982
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 HRESULT rc = checkStateDependency(MutableStateDep);
2986 if (FAILED(rc)) return rc;
2987
2988 setModified(IsModified_MachineData);
2989 mUserData.backup();
2990 mUserData->s.strTeleporterAddress = aAddress;
2991
2992 return S_OK;
2993}
2994
2995STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
2996{
2997 CheckComArgOutPointerValid(aPassword);
2998
2999 AutoCaller autoCaller(this);
3000 HRESULT hrc = autoCaller.rc();
3001 if (SUCCEEDED(hrc))
3002 {
3003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3004 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3005 }
3006
3007 return hrc;
3008}
3009
3010STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3011{
3012 /*
3013 * Hash the password first.
3014 */
3015 Utf8Str strPassword(aPassword);
3016 if (!strPassword.isEmpty())
3017 {
3018 if (VBoxIsPasswordHashed(&strPassword))
3019 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3020 VBoxHashPassword(&strPassword);
3021 }
3022
3023 /*
3024 * Do the update.
3025 */
3026 AutoCaller autoCaller(this);
3027 HRESULT hrc = autoCaller.rc();
3028 if (SUCCEEDED(hrc))
3029 {
3030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3031 hrc = checkStateDependency(MutableStateDep);
3032 if (SUCCEEDED(hrc))
3033 {
3034 setModified(IsModified_MachineData);
3035 mUserData.backup();
3036 mUserData->s.strTeleporterPassword = strPassword;
3037 }
3038 }
3039
3040 return hrc;
3041}
3042
3043STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3044{
3045 CheckComArgOutPointerValid(aState);
3046
3047 AutoCaller autoCaller(this);
3048 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3049
3050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3051
3052 *aState = mUserData->s.enmFaultToleranceState;
3053 return S_OK;
3054}
3055
3056STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3057{
3058 AutoCaller autoCaller(this);
3059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3060
3061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 /* @todo deal with running state change. */
3064 HRESULT rc = checkStateDependency(MutableStateDep);
3065 if (FAILED(rc)) return rc;
3066
3067 setModified(IsModified_MachineData);
3068 mUserData.backup();
3069 mUserData->s.enmFaultToleranceState = aState;
3070 return S_OK;
3071}
3072
3073STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3074{
3075 CheckComArgOutPointerValid(aAddress);
3076
3077 AutoCaller autoCaller(this);
3078 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3079
3080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3081
3082 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3083 return S_OK;
3084}
3085
3086STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3087{
3088 AutoCaller autoCaller(this);
3089 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3090
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 /* @todo deal with running state change. */
3094 HRESULT rc = checkStateDependency(MutableStateDep);
3095 if (FAILED(rc)) return rc;
3096
3097 setModified(IsModified_MachineData);
3098 mUserData.backup();
3099 mUserData->s.strFaultToleranceAddress = aAddress;
3100 return S_OK;
3101}
3102
3103STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3104{
3105 CheckComArgOutPointerValid(aPort);
3106
3107 AutoCaller autoCaller(this);
3108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3109
3110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3111
3112 *aPort = mUserData->s.uFaultTolerancePort;
3113 return S_OK;
3114}
3115
3116STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3117{
3118 AutoCaller autoCaller(this);
3119 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3120
3121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3122
3123 /* @todo deal with running state change. */
3124 HRESULT rc = checkStateDependency(MutableStateDep);
3125 if (FAILED(rc)) return rc;
3126
3127 setModified(IsModified_MachineData);
3128 mUserData.backup();
3129 mUserData->s.uFaultTolerancePort = aPort;
3130 return S_OK;
3131}
3132
3133STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3134{
3135 CheckComArgOutPointerValid(aPassword);
3136
3137 AutoCaller autoCaller(this);
3138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3139
3140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3141
3142 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3143
3144 return S_OK;
3145}
3146
3147STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3148{
3149 AutoCaller autoCaller(this);
3150 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3151
3152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 /* @todo deal with running state change. */
3155 HRESULT rc = checkStateDependency(MutableStateDep);
3156 if (FAILED(rc)) return rc;
3157
3158 setModified(IsModified_MachineData);
3159 mUserData.backup();
3160 mUserData->s.strFaultTolerancePassword = aPassword;
3161
3162 return S_OK;
3163}
3164
3165STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3166{
3167 CheckComArgOutPointerValid(aInterval);
3168
3169 AutoCaller autoCaller(this);
3170 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3171
3172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3173
3174 *aInterval = mUserData->s.uFaultToleranceInterval;
3175 return S_OK;
3176}
3177
3178STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3179{
3180 AutoCaller autoCaller(this);
3181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3182
3183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 /* @todo deal with running state change. */
3186 HRESULT rc = checkStateDependency(MutableStateDep);
3187 if (FAILED(rc)) return rc;
3188
3189 setModified(IsModified_MachineData);
3190 mUserData.backup();
3191 mUserData->s.uFaultToleranceInterval = aInterval;
3192 return S_OK;
3193}
3194
3195STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3196{
3197 CheckComArgOutPointerValid(aEnabled);
3198
3199 AutoCaller autoCaller(this);
3200 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3201
3202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3203
3204 *aEnabled = mUserData->s.fRTCUseUTC;
3205
3206 return S_OK;
3207}
3208
3209STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3210{
3211 AutoCaller autoCaller(this);
3212 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3213
3214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3215
3216 /* Only allow it to be set to true when PoweredOff or Aborted.
3217 (Clearing it is always permitted.) */
3218 if ( aEnabled
3219 && mData->mRegistered
3220 && ( !isSessionMachine()
3221 || ( mData->mMachineState != MachineState_PoweredOff
3222 && mData->mMachineState != MachineState_Teleported
3223 && mData->mMachineState != MachineState_Aborted
3224 )
3225 )
3226 )
3227 return setError(VBOX_E_INVALID_VM_STATE,
3228 tr("The machine is not powered off (state is %s)"),
3229 Global::stringifyMachineState(mData->mMachineState));
3230
3231 setModified(IsModified_MachineData);
3232 mUserData.backup();
3233 mUserData->s.fRTCUseUTC = !!aEnabled;
3234
3235 return S_OK;
3236}
3237
3238STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3239{
3240 CheckComArgOutPointerValid(aEnabled);
3241
3242 AutoCaller autoCaller(this);
3243 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3244
3245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3246
3247 *aEnabled = mHWData->mIOCacheEnabled;
3248
3249 return S_OK;
3250}
3251
3252STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3253{
3254 AutoCaller autoCaller(this);
3255 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3256
3257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3258
3259 HRESULT rc = checkStateDependency(MutableStateDep);
3260 if (FAILED(rc)) return rc;
3261
3262 setModified(IsModified_MachineData);
3263 mHWData.backup();
3264 mHWData->mIOCacheEnabled = aEnabled;
3265
3266 return S_OK;
3267}
3268
3269STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3270{
3271 CheckComArgOutPointerValid(aIOCacheSize);
3272
3273 AutoCaller autoCaller(this);
3274 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3275
3276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3277
3278 *aIOCacheSize = mHWData->mIOCacheSize;
3279
3280 return S_OK;
3281}
3282
3283STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3284{
3285 AutoCaller autoCaller(this);
3286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3287
3288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3289
3290 HRESULT rc = checkStateDependency(MutableStateDep);
3291 if (FAILED(rc)) return rc;
3292
3293 setModified(IsModified_MachineData);
3294 mHWData.backup();
3295 mHWData->mIOCacheSize = aIOCacheSize;
3296
3297 return S_OK;
3298}
3299
3300
3301/**
3302 * @note Locks objects!
3303 */
3304STDMETHODIMP Machine::LockMachine(ISession *aSession,
3305 LockType_T lockType)
3306{
3307 CheckComArgNotNull(aSession);
3308
3309 AutoCaller autoCaller(this);
3310 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3311
3312 /* check the session state */
3313 SessionState_T state;
3314 HRESULT rc = aSession->COMGETTER(State)(&state);
3315 if (FAILED(rc)) return rc;
3316
3317 if (state != SessionState_Unlocked)
3318 return setError(VBOX_E_INVALID_OBJECT_STATE,
3319 tr("The given session is busy"));
3320
3321 // get the client's IInternalSessionControl interface
3322 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3323 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3324 E_INVALIDARG);
3325
3326 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3327
3328 if (!mData->mRegistered)
3329 return setError(E_UNEXPECTED,
3330 tr("The machine '%s' is not registered"),
3331 mUserData->s.strName.c_str());
3332
3333 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3334
3335 SessionState_T oldState = mData->mSession.mState;
3336 /* Hack: in case the session is closing and there is a progress object
3337 * which allows waiting for the session to be closed, take the opportunity
3338 * and do a limited wait (max. 1 second). This helps a lot when the system
3339 * is busy and thus session closing can take a little while. */
3340 if ( mData->mSession.mState == SessionState_Unlocking
3341 && mData->mSession.mProgress)
3342 {
3343 alock.release();
3344 mData->mSession.mProgress->WaitForCompletion(1000);
3345 alock.acquire();
3346 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3347 }
3348
3349 // try again now
3350 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3351 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3352 )
3353 {
3354 // OK, share the session... we are now dealing with three processes:
3355 // 1) VBoxSVC (where this code runs);
3356 // 2) process C: the caller's client process (who wants a shared session);
3357 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3358
3359 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3360 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3361 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3362 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3363 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3364
3365 /*
3366 * Release the lock before calling the client process. It's safe here
3367 * since the only thing to do after we get the lock again is to add
3368 * the remote control to the list (which doesn't directly influence
3369 * anything).
3370 */
3371 alock.release();
3372
3373 // get the console of the session holding the write lock (this is a remote call)
3374 ComPtr<IConsole> pConsoleW;
3375 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3376 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3377 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3378 if (FAILED(rc))
3379 // the failure may occur w/o any error info (from RPC), so provide one
3380 return setError(VBOX_E_VM_ERROR,
3381 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3382
3383 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3384
3385 // share the session machine and W's console with the caller's session
3386 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3387 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3388 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3389
3390 if (FAILED(rc))
3391 // the failure may occur w/o any error info (from RPC), so provide one
3392 return setError(VBOX_E_VM_ERROR,
3393 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3394 alock.acquire();
3395
3396 // need to revalidate the state after acquiring the lock again
3397 if (mData->mSession.mState != SessionState_Locked)
3398 {
3399 pSessionControl->Uninitialize();
3400 return setError(VBOX_E_INVALID_SESSION_STATE,
3401 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3402 mUserData->s.strName.c_str());
3403 }
3404
3405 // add the caller's session to the list
3406 mData->mSession.mRemoteControls.push_back(pSessionControl);
3407 }
3408 else if ( mData->mSession.mState == SessionState_Locked
3409 || mData->mSession.mState == SessionState_Unlocking
3410 )
3411 {
3412 // sharing not permitted, or machine still unlocking:
3413 return setError(VBOX_E_INVALID_OBJECT_STATE,
3414 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3415 mUserData->s.strName.c_str());
3416 }
3417 else
3418 {
3419 // machine is not locked: then write-lock the machine (create the session machine)
3420
3421 // must not be busy
3422 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3423
3424 // get the caller's session PID
3425 RTPROCESS pid = NIL_RTPROCESS;
3426 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3427 pSessionControl->GetPID((ULONG*)&pid);
3428 Assert(pid != NIL_RTPROCESS);
3429
3430 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3431
3432 if (fLaunchingVMProcess)
3433 {
3434 // this machine is awaiting for a spawning session to be opened:
3435 // then the calling process must be the one that got started by
3436 // LaunchVMProcess()
3437
3438 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3439 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3440
3441 if (mData->mSession.mPID != pid)
3442 return setError(E_ACCESSDENIED,
3443 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3444 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3445 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3446 }
3447
3448 // create the mutable SessionMachine from the current machine
3449 ComObjPtr<SessionMachine> sessionMachine;
3450 sessionMachine.createObject();
3451 rc = sessionMachine->init(this);
3452 AssertComRC(rc);
3453
3454 /* NOTE: doing return from this function after this point but
3455 * before the end is forbidden since it may call SessionMachine::uninit()
3456 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3457 * lock while still holding the Machine lock in alock so that a deadlock
3458 * is possible due to the wrong lock order. */
3459
3460 if (SUCCEEDED(rc))
3461 {
3462 /*
3463 * Set the session state to Spawning to protect against subsequent
3464 * attempts to open a session and to unregister the machine after
3465 * we release the lock.
3466 */
3467 SessionState_T origState = mData->mSession.mState;
3468 mData->mSession.mState = SessionState_Spawning;
3469
3470 /*
3471 * Release the lock before calling the client process -- it will call
3472 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3473 * because the state is Spawning, so that LaunchVMProcess() and
3474 * LockMachine() calls will fail. This method, called before we
3475 * acquire the lock again, will fail because of the wrong PID.
3476 *
3477 * Note that mData->mSession.mRemoteControls accessed outside
3478 * the lock may not be modified when state is Spawning, so it's safe.
3479 */
3480 alock.release();
3481
3482 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3483 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3484 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3485
3486 /* The failure may occur w/o any error info (from RPC), so provide one */
3487 if (FAILED(rc))
3488 setError(VBOX_E_VM_ERROR,
3489 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3490
3491 if ( SUCCEEDED(rc)
3492 && fLaunchingVMProcess
3493 )
3494 {
3495 /* complete the remote session initialization */
3496
3497 /* get the console from the direct session */
3498 ComPtr<IConsole> console;
3499 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3500 ComAssertComRC(rc);
3501
3502 if (SUCCEEDED(rc) && !console)
3503 {
3504 ComAssert(!!console);
3505 rc = E_FAIL;
3506 }
3507
3508 /* assign machine & console to the remote session */
3509 if (SUCCEEDED(rc))
3510 {
3511 /*
3512 * after LaunchVMProcess(), the first and the only
3513 * entry in remoteControls is that remote session
3514 */
3515 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3516 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3517 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3518
3519 /* The failure may occur w/o any error info (from RPC), so provide one */
3520 if (FAILED(rc))
3521 setError(VBOX_E_VM_ERROR,
3522 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3523 }
3524
3525 if (FAILED(rc))
3526 pSessionControl->Uninitialize();
3527 }
3528
3529 /* acquire the lock again */
3530 alock.acquire();
3531
3532 /* Restore the session state */
3533 mData->mSession.mState = origState;
3534 }
3535
3536 // finalize spawning anyway (this is why we don't return on errors above)
3537 if (fLaunchingVMProcess)
3538 {
3539 /* Note that the progress object is finalized later */
3540 /** @todo Consider checking mData->mSession.mProgress for cancellation
3541 * around here. */
3542
3543 /* We don't reset mSession.mPID here because it is necessary for
3544 * SessionMachine::uninit() to reap the child process later. */
3545
3546 if (FAILED(rc))
3547 {
3548 /* Close the remote session, remove the remote control from the list
3549 * and reset session state to Closed (@note keep the code in sync
3550 * with the relevant part in openSession()). */
3551
3552 Assert(mData->mSession.mRemoteControls.size() == 1);
3553 if (mData->mSession.mRemoteControls.size() == 1)
3554 {
3555 ErrorInfoKeeper eik;
3556 mData->mSession.mRemoteControls.front()->Uninitialize();
3557 }
3558
3559 mData->mSession.mRemoteControls.clear();
3560 mData->mSession.mState = SessionState_Unlocked;
3561 }
3562 }
3563 else
3564 {
3565 /* memorize PID of the directly opened session */
3566 if (SUCCEEDED(rc))
3567 mData->mSession.mPID = pid;
3568 }
3569
3570 if (SUCCEEDED(rc))
3571 {
3572 /* memorize the direct session control and cache IUnknown for it */
3573 mData->mSession.mDirectControl = pSessionControl;
3574 mData->mSession.mState = SessionState_Locked;
3575 /* associate the SessionMachine with this Machine */
3576 mData->mSession.mMachine = sessionMachine;
3577
3578 /* request an IUnknown pointer early from the remote party for later
3579 * identity checks (it will be internally cached within mDirectControl
3580 * at least on XPCOM) */
3581 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3582 NOREF(unk);
3583 }
3584
3585 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3586 * would break the lock order */
3587 alock.release();
3588
3589 /* uninitialize the created session machine on failure */
3590 if (FAILED(rc))
3591 sessionMachine->uninit();
3592
3593 }
3594
3595 if (SUCCEEDED(rc))
3596 {
3597 /*
3598 * tell the client watcher thread to update the set of
3599 * machines that have open sessions
3600 */
3601 mParent->updateClientWatcher();
3602
3603 if (oldState != SessionState_Locked)
3604 /* fire an event */
3605 mParent->onSessionStateChange(getId(), SessionState_Locked);
3606 }
3607
3608 return rc;
3609}
3610
3611/**
3612 * @note Locks objects!
3613 */
3614STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3615 IN_BSTR aFrontend,
3616 IN_BSTR aEnvironment,
3617 IProgress **aProgress)
3618{
3619 CheckComArgStr(aFrontend);
3620 Utf8Str strFrontend(aFrontend);
3621 Utf8Str strEnvironment(aEnvironment);
3622 /* "emergencystop" doesn't need the session, so skip the checks/interface
3623 * retrieval. This code doesn't quite fit in here, but introducing a
3624 * special API method would be even more effort, and would require explicit
3625 * support by every API client. It's better to hide the feature a bit. */
3626 if (strFrontend != "emergencystop")
3627 CheckComArgNotNull(aSession);
3628 CheckComArgOutPointerValid(aProgress);
3629
3630 AutoCaller autoCaller(this);
3631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3632
3633 HRESULT rc = S_OK;
3634 if (strFrontend.isEmpty())
3635 {
3636 Bstr bstrFrontend;
3637 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3638 if (FAILED(rc))
3639 return rc;
3640 strFrontend = bstrFrontend;
3641 if (strFrontend.isEmpty())
3642 {
3643 ComPtr<ISystemProperties> systemProperties;
3644 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3645 if (FAILED(rc))
3646 return rc;
3647 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3648 if (FAILED(rc))
3649 return rc;
3650 strFrontend = bstrFrontend;
3651 }
3652 /* paranoia - emergencystop is not a valid default */
3653 if (strFrontend == "emergencystop")
3654 strFrontend = Utf8Str::Empty;
3655 }
3656
3657 if (strFrontend != "emergencystop")
3658 {
3659 /* check the session state */
3660 SessionState_T state;
3661 rc = aSession->COMGETTER(State)(&state);
3662 if (FAILED(rc))
3663 return rc;
3664
3665 if (state != SessionState_Unlocked)
3666 return setError(VBOX_E_INVALID_OBJECT_STATE,
3667 tr("The given session is busy"));
3668
3669 /* get the IInternalSessionControl interface */
3670 ComPtr<IInternalSessionControl> control(aSession);
3671 ComAssertMsgRet(!control.isNull(),
3672 ("No IInternalSessionControl interface"),
3673 E_INVALIDARG);
3674
3675 /* get the teleporter enable state for the progress object init. */
3676 BOOL fTeleporterEnabled;
3677 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3678 if (FAILED(rc))
3679 return rc;
3680
3681 /* create a progress object */
3682 ComObjPtr<ProgressProxy> progress;
3683 progress.createObject();
3684 rc = progress->init(mParent,
3685 static_cast<IMachine*>(this),
3686 Bstr(tr("Starting VM")).raw(),
3687 TRUE /* aCancelable */,
3688 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3689 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3690 2 /* uFirstOperationWeight */,
3691 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3692
3693 if (SUCCEEDED(rc))
3694 {
3695 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3696 if (SUCCEEDED(rc))
3697 {
3698 progress.queryInterfaceTo(aProgress);
3699
3700 /* signal the client watcher thread */
3701 mParent->updateClientWatcher();
3702
3703 /* fire an event */
3704 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3705 }
3706 }
3707 }
3708 else
3709 {
3710 /* no progress object - either instant success or failure */
3711 *aProgress = NULL;
3712
3713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3714
3715 if (mData->mSession.mState != SessionState_Locked)
3716 return setError(VBOX_E_INVALID_OBJECT_STATE,
3717 tr("The machine '%s' is not locked by a session"),
3718 mUserData->s.strName.c_str());
3719
3720 /* must have a VM process associated - do not kill normal API clients
3721 * with an open session */
3722 if (!Global::IsOnline(mData->mMachineState))
3723 return setError(VBOX_E_INVALID_OBJECT_STATE,
3724 tr("The machine '%s' does not have a VM process"),
3725 mUserData->s.strName.c_str());
3726
3727 /* forcibly terminate the VM process */
3728 if (mData->mSession.mPID != NIL_RTPROCESS)
3729 RTProcTerminate(mData->mSession.mPID);
3730
3731 /* signal the client watcher thread, as most likely the client has
3732 * been terminated */
3733 mParent->updateClientWatcher();
3734 }
3735
3736 return rc;
3737}
3738
3739STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3740{
3741 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3742 return setError(E_INVALIDARG,
3743 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3744 aPosition, SchemaDefs::MaxBootPosition);
3745
3746 if (aDevice == DeviceType_USB)
3747 return setError(E_NOTIMPL,
3748 tr("Booting from USB device is currently not supported"));
3749
3750 AutoCaller autoCaller(this);
3751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3752
3753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3754
3755 HRESULT rc = checkStateDependency(MutableStateDep);
3756 if (FAILED(rc)) return rc;
3757
3758 setModified(IsModified_MachineData);
3759 mHWData.backup();
3760 mHWData->mBootOrder[aPosition - 1] = aDevice;
3761
3762 return S_OK;
3763}
3764
3765STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3766{
3767 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3768 return setError(E_INVALIDARG,
3769 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3770 aPosition, SchemaDefs::MaxBootPosition);
3771
3772 AutoCaller autoCaller(this);
3773 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3774
3775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3776
3777 *aDevice = mHWData->mBootOrder[aPosition - 1];
3778
3779 return S_OK;
3780}
3781
3782STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3783 LONG aControllerPort,
3784 LONG aDevice,
3785 DeviceType_T aType,
3786 IMedium *aMedium)
3787{
3788 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3789 aControllerName, aControllerPort, aDevice, aType, aMedium));
3790
3791 CheckComArgStrNotEmptyOrNull(aControllerName);
3792
3793 AutoCaller autoCaller(this);
3794 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3795
3796 // request the host lock first, since might be calling Host methods for getting host drives;
3797 // next, protect the media tree all the while we're in here, as well as our member variables
3798 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3799 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3800
3801 HRESULT rc = checkStateDependency(MutableStateDep);
3802 if (FAILED(rc)) return rc;
3803
3804 /// @todo NEWMEDIA implicit machine registration
3805 if (!mData->mRegistered)
3806 return setError(VBOX_E_INVALID_OBJECT_STATE,
3807 tr("Cannot attach storage devices to an unregistered machine"));
3808
3809 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3810
3811 /* Check for an existing controller. */
3812 ComObjPtr<StorageController> ctl;
3813 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3814 if (FAILED(rc)) return rc;
3815
3816 StorageControllerType_T ctrlType;
3817 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3818 if (FAILED(rc))
3819 return setError(E_FAIL,
3820 tr("Could not get type of controller '%ls'"),
3821 aControllerName);
3822
3823 bool fSilent = false;
3824 Utf8Str strReconfig;
3825
3826 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3827 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3828 if (FAILED(rc))
3829 return rc;
3830 if ( mData->mMachineState == MachineState_Paused
3831 && strReconfig == "1")
3832 fSilent = true;
3833
3834 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3835 bool fHotplug = false;
3836 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3837 fHotplug = true;
3838
3839 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3840 return setError(VBOX_E_INVALID_VM_STATE,
3841 tr("Controller '%ls' does not support hotplugging"),
3842 aControllerName);
3843
3844 // check that the port and device are not out of range
3845 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3846 if (FAILED(rc)) return rc;
3847
3848 /* check if the device slot is already busy */
3849 MediumAttachment *pAttachTemp;
3850 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3851 aControllerName,
3852 aControllerPort,
3853 aDevice)))
3854 {
3855 Medium *pMedium = pAttachTemp->getMedium();
3856 if (pMedium)
3857 {
3858 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3859 return setError(VBOX_E_OBJECT_IN_USE,
3860 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3861 pMedium->getLocationFull().c_str(),
3862 aControllerPort,
3863 aDevice,
3864 aControllerName);
3865 }
3866 else
3867 return setError(VBOX_E_OBJECT_IN_USE,
3868 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3869 aControllerPort, aDevice, aControllerName);
3870 }
3871
3872 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3873 if (aMedium && medium.isNull())
3874 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3875
3876 AutoCaller mediumCaller(medium);
3877 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3878
3879 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3880
3881 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3882 && !medium.isNull()
3883 )
3884 return setError(VBOX_E_OBJECT_IN_USE,
3885 tr("Medium '%s' is already attached to this virtual machine"),
3886 medium->getLocationFull().c_str());
3887
3888 if (!medium.isNull())
3889 {
3890 MediumType_T mtype = medium->getType();
3891 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3892 // For DVDs it's not written to the config file, so needs no global config
3893 // version bump. For floppies it's a new attribute "type", which is ignored
3894 // by older VirtualBox version, so needs no global config version bump either.
3895 // For hard disks this type is not accepted.
3896 if (mtype == MediumType_MultiAttach)
3897 {
3898 // This type is new with VirtualBox 4.0 and therefore requires settings
3899 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3900 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3901 // two reasons: The medium type is a property of the media registry tree, which
3902 // can reside in the global config file (for pre-4.0 media); we would therefore
3903 // possibly need to bump the global config version. We don't want to do that though
3904 // because that might make downgrading to pre-4.0 impossible.
3905 // As a result, we can only use these two new types if the medium is NOT in the
3906 // global registry:
3907 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3908 if ( medium->isInRegistry(uuidGlobalRegistry)
3909 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3910 )
3911 return setError(VBOX_E_INVALID_OBJECT_STATE,
3912 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3913 "to machines that were created with VirtualBox 4.0 or later"),
3914 medium->getLocationFull().c_str());
3915 }
3916 }
3917
3918 bool fIndirect = false;
3919 if (!medium.isNull())
3920 fIndirect = medium->isReadOnly();
3921 bool associate = true;
3922
3923 do
3924 {
3925 if ( aType == DeviceType_HardDisk
3926 && mMediaData.isBackedUp())
3927 {
3928 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3929
3930 /* check if the medium was attached to the VM before we started
3931 * changing attachments in which case the attachment just needs to
3932 * be restored */
3933 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3934 {
3935 AssertReturn(!fIndirect, E_FAIL);
3936
3937 /* see if it's the same bus/channel/device */
3938 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3939 {
3940 /* the simplest case: restore the whole attachment
3941 * and return, nothing else to do */
3942 mMediaData->mAttachments.push_back(pAttachTemp);
3943 return S_OK;
3944 }
3945
3946 /* bus/channel/device differ; we need a new attachment object,
3947 * but don't try to associate it again */
3948 associate = false;
3949 break;
3950 }
3951 }
3952
3953 /* go further only if the attachment is to be indirect */
3954 if (!fIndirect)
3955 break;
3956
3957 /* perform the so called smart attachment logic for indirect
3958 * attachments. Note that smart attachment is only applicable to base
3959 * hard disks. */
3960
3961 if (medium->getParent().isNull())
3962 {
3963 /* first, investigate the backup copy of the current hard disk
3964 * attachments to make it possible to re-attach existing diffs to
3965 * another device slot w/o losing their contents */
3966 if (mMediaData.isBackedUp())
3967 {
3968 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3969
3970 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3971 uint32_t foundLevel = 0;
3972
3973 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
3974 it != oldAtts.end();
3975 ++it)
3976 {
3977 uint32_t level = 0;
3978 MediumAttachment *pAttach = *it;
3979 ComObjPtr<Medium> pMedium = pAttach->getMedium();
3980 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
3981 if (pMedium.isNull())
3982 continue;
3983
3984 if (pMedium->getBase(&level) == medium)
3985 {
3986 /* skip the hard disk if its currently attached (we
3987 * cannot attach the same hard disk twice) */
3988 if (findAttachment(mMediaData->mAttachments,
3989 pMedium))
3990 continue;
3991
3992 /* matched device, channel and bus (i.e. attached to the
3993 * same place) will win and immediately stop the search;
3994 * otherwise the attachment that has the youngest
3995 * descendant of medium will be used
3996 */
3997 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
3998 {
3999 /* the simplest case: restore the whole attachment
4000 * and return, nothing else to do */
4001 mMediaData->mAttachments.push_back(*it);
4002 return S_OK;
4003 }
4004 else if ( foundIt == oldAtts.end()
4005 || level > foundLevel /* prefer younger */
4006 )
4007 {
4008 foundIt = it;
4009 foundLevel = level;
4010 }
4011 }
4012 }
4013
4014 if (foundIt != oldAtts.end())
4015 {
4016 /* use the previously attached hard disk */
4017 medium = (*foundIt)->getMedium();
4018 mediumCaller.attach(medium);
4019 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4020 mediumLock.attach(medium);
4021 /* not implicit, doesn't require association with this VM */
4022 fIndirect = false;
4023 associate = false;
4024 /* go right to the MediumAttachment creation */
4025 break;
4026 }
4027 }
4028
4029 /* must give up the medium lock and medium tree lock as below we
4030 * go over snapshots, which needs a lock with higher lock order. */
4031 mediumLock.release();
4032 treeLock.release();
4033
4034 /* then, search through snapshots for the best diff in the given
4035 * hard disk's chain to base the new diff on */
4036
4037 ComObjPtr<Medium> base;
4038 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4039 while (snap)
4040 {
4041 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4042
4043 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4044
4045 MediumAttachment *pAttachFound = NULL;
4046 uint32_t foundLevel = 0;
4047
4048 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4049 it != snapAtts.end();
4050 ++it)
4051 {
4052 MediumAttachment *pAttach = *it;
4053 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4054 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4055 if (pMedium.isNull())
4056 continue;
4057
4058 uint32_t level = 0;
4059 if (pMedium->getBase(&level) == medium)
4060 {
4061 /* matched device, channel and bus (i.e. attached to the
4062 * same place) will win and immediately stop the search;
4063 * otherwise the attachment that has the youngest
4064 * descendant of medium will be used
4065 */
4066 if ( pAttach->getDevice() == aDevice
4067 && pAttach->getPort() == aControllerPort
4068 && pAttach->getControllerName() == aControllerName
4069 )
4070 {
4071 pAttachFound = pAttach;
4072 break;
4073 }
4074 else if ( !pAttachFound
4075 || level > foundLevel /* prefer younger */
4076 )
4077 {
4078 pAttachFound = pAttach;
4079 foundLevel = level;
4080 }
4081 }
4082 }
4083
4084 if (pAttachFound)
4085 {
4086 base = pAttachFound->getMedium();
4087 break;
4088 }
4089
4090 snap = snap->getParent();
4091 }
4092
4093 /* re-lock medium tree and the medium, as we need it below */
4094 treeLock.acquire();
4095 mediumLock.acquire();
4096
4097 /* found a suitable diff, use it as a base */
4098 if (!base.isNull())
4099 {
4100 medium = base;
4101 mediumCaller.attach(medium);
4102 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4103 mediumLock.attach(medium);
4104 }
4105 }
4106
4107 Utf8Str strFullSnapshotFolder;
4108 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4109
4110 ComObjPtr<Medium> diff;
4111 diff.createObject();
4112 // store this diff in the same registry as the parent
4113 Guid uuidRegistryParent;
4114 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4115 {
4116 // parent image has no registry: this can happen if we're attaching a new immutable
4117 // image that has not yet been attached (medium then points to the base and we're
4118 // creating the diff image for the immutable, and the parent is not yet registered);
4119 // put the parent in the machine registry then
4120 mediumLock.release();
4121 treeLock.release();
4122 alock.release();
4123 addMediumToRegistry(medium);
4124 alock.acquire();
4125 treeLock.acquire();
4126 mediumLock.acquire();
4127 medium->getFirstRegistryMachineId(uuidRegistryParent);
4128 }
4129 rc = diff->init(mParent,
4130 medium->getPreferredDiffFormat(),
4131 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4132 uuidRegistryParent);
4133 if (FAILED(rc)) return rc;
4134
4135 /* Apply the normal locking logic to the entire chain. */
4136 MediumLockList *pMediumLockList(new MediumLockList());
4137 mediumLock.release();
4138 treeLock.release();
4139 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4140 true /* fMediumLockWrite */,
4141 medium,
4142 *pMediumLockList);
4143 treeLock.acquire();
4144 mediumLock.acquire();
4145 if (SUCCEEDED(rc))
4146 {
4147 mediumLock.release();
4148 treeLock.release();
4149 rc = pMediumLockList->Lock();
4150 treeLock.acquire();
4151 mediumLock.acquire();
4152 if (FAILED(rc))
4153 setError(rc,
4154 tr("Could not lock medium when creating diff '%s'"),
4155 diff->getLocationFull().c_str());
4156 else
4157 {
4158 /* will release the lock before the potentially lengthy
4159 * operation, so protect with the special state */
4160 MachineState_T oldState = mData->mMachineState;
4161 setMachineState(MachineState_SettingUp);
4162
4163 mediumLock.release();
4164 treeLock.release();
4165 alock.release();
4166
4167 rc = medium->createDiffStorage(diff,
4168 MediumVariant_Standard,
4169 pMediumLockList,
4170 NULL /* aProgress */,
4171 true /* aWait */);
4172
4173 alock.acquire();
4174 treeLock.acquire();
4175 mediumLock.acquire();
4176
4177 setMachineState(oldState);
4178 }
4179 }
4180
4181 /* Unlock the media and free the associated memory. */
4182 delete pMediumLockList;
4183
4184 if (FAILED(rc)) return rc;
4185
4186 /* use the created diff for the actual attachment */
4187 medium = diff;
4188 mediumCaller.attach(medium);
4189 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4190 mediumLock.attach(medium);
4191 }
4192 while (0);
4193
4194 ComObjPtr<MediumAttachment> attachment;
4195 attachment.createObject();
4196 rc = attachment->init(this,
4197 medium,
4198 aControllerName,
4199 aControllerPort,
4200 aDevice,
4201 aType,
4202 fIndirect,
4203 false /* fPassthrough */,
4204 false /* fTempEject */,
4205 false /* fNonRotational */,
4206 false /* fDiscard */,
4207 Utf8Str::Empty);
4208 if (FAILED(rc)) return rc;
4209
4210 if (associate && !medium.isNull())
4211 {
4212 // as the last step, associate the medium to the VM
4213 rc = medium->addBackReference(mData->mUuid);
4214 // here we can fail because of Deleting, or being in process of creating a Diff
4215 if (FAILED(rc)) return rc;
4216
4217 mediumLock.release();
4218 treeLock.release();
4219 alock.release();
4220 addMediumToRegistry(medium);
4221 alock.acquire();
4222 treeLock.acquire();
4223 mediumLock.acquire();
4224 }
4225
4226 /* success: finally remember the attachment */
4227 setModified(IsModified_Storage);
4228 mMediaData.backup();
4229 mMediaData->mAttachments.push_back(attachment);
4230
4231 mediumLock.release();
4232 treeLock.release();
4233 alock.release();
4234
4235 if (fHotplug || fSilent)
4236 {
4237 MediumLockList *pMediumLockList(new MediumLockList());
4238
4239 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4240 true /* fMediumLockWrite */,
4241 NULL,
4242 *pMediumLockList);
4243 alock.acquire();
4244 if (FAILED(rc))
4245 delete pMediumLockList;
4246 else
4247 {
4248 mData->mSession.mLockedMedia.Unlock();
4249 alock.release();
4250 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4251 mData->mSession.mLockedMedia.Lock();
4252 alock.acquire();
4253 }
4254 alock.release();
4255
4256 if (SUCCEEDED(rc))
4257 {
4258 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4259 /* Remove lock list in case of error. */
4260 if (FAILED(rc))
4261 {
4262 mData->mSession.mLockedMedia.Unlock();
4263 mData->mSession.mLockedMedia.Remove(attachment);
4264 mData->mSession.mLockedMedia.Lock();
4265 }
4266 }
4267 }
4268
4269 mParent->saveModifiedRegistries();
4270
4271 return rc;
4272}
4273
4274STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4275 LONG aDevice)
4276{
4277 CheckComArgStrNotEmptyOrNull(aControllerName);
4278
4279 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4280 aControllerName, aControllerPort, aDevice));
4281
4282 AutoCaller autoCaller(this);
4283 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4284
4285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4286
4287 HRESULT rc = checkStateDependency(MutableStateDep);
4288 if (FAILED(rc)) return rc;
4289
4290 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4291
4292 /* Check for an existing controller. */
4293 ComObjPtr<StorageController> ctl;
4294 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4295 if (FAILED(rc)) return rc;
4296
4297 StorageControllerType_T ctrlType;
4298 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4299 if (FAILED(rc))
4300 return setError(E_FAIL,
4301 tr("Could not get type of controller '%ls'"),
4302 aControllerName);
4303
4304 bool fSilent = false;
4305 Utf8Str strReconfig;
4306
4307 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4308 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4309 if (FAILED(rc))
4310 return rc;
4311 if ( mData->mMachineState == MachineState_Paused
4312 && strReconfig == "1")
4313 fSilent = true;
4314
4315 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4316 bool fHotplug = false;
4317 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4318 fHotplug = true;
4319
4320 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4321 return setError(VBOX_E_INVALID_VM_STATE,
4322 tr("Controller '%ls' does not support hotplugging"),
4323 aControllerName);
4324
4325 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4326 aControllerName,
4327 aControllerPort,
4328 aDevice);
4329 if (!pAttach)
4330 return setError(VBOX_E_OBJECT_NOT_FOUND,
4331 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4332 aDevice, aControllerPort, aControllerName);
4333
4334 /*
4335 * The VM has to detach the device before we delete any implicit diffs.
4336 * If this fails we can roll back without loosing data.
4337 */
4338 if (fHotplug || fSilent)
4339 {
4340 alock.release();
4341 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4342 alock.acquire();
4343 }
4344 if (FAILED(rc)) return rc;
4345
4346 /* If we are here everything went well and we can delete the implicit now. */
4347 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4348
4349 alock.release();
4350
4351 mParent->saveModifiedRegistries();
4352
4353 return rc;
4354}
4355
4356STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4357 LONG aDevice, BOOL aPassthrough)
4358{
4359 CheckComArgStrNotEmptyOrNull(aControllerName);
4360
4361 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4362 aControllerName, aControllerPort, aDevice, aPassthrough));
4363
4364 AutoCaller autoCaller(this);
4365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4366
4367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4368
4369 HRESULT rc = checkStateDependency(MutableStateDep);
4370 if (FAILED(rc)) return rc;
4371
4372 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4373
4374 if (Global::IsOnlineOrTransient(mData->mMachineState))
4375 return setError(VBOX_E_INVALID_VM_STATE,
4376 tr("Invalid machine state: %s"),
4377 Global::stringifyMachineState(mData->mMachineState));
4378
4379 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4380 aControllerName,
4381 aControllerPort,
4382 aDevice);
4383 if (!pAttach)
4384 return setError(VBOX_E_OBJECT_NOT_FOUND,
4385 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4386 aDevice, aControllerPort, aControllerName);
4387
4388
4389 setModified(IsModified_Storage);
4390 mMediaData.backup();
4391
4392 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4393
4394 if (pAttach->getType() != DeviceType_DVD)
4395 return setError(E_INVALIDARG,
4396 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4397 aDevice, aControllerPort, aControllerName);
4398 pAttach->updatePassthrough(!!aPassthrough);
4399
4400 return S_OK;
4401}
4402
4403STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4404 LONG aDevice, BOOL aTemporaryEject)
4405{
4406 CheckComArgStrNotEmptyOrNull(aControllerName);
4407
4408 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4409 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4410
4411 AutoCaller autoCaller(this);
4412 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4413
4414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4415
4416 HRESULT rc = checkStateDependency(MutableStateDep);
4417 if (FAILED(rc)) return rc;
4418
4419 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4420 aControllerName,
4421 aControllerPort,
4422 aDevice);
4423 if (!pAttach)
4424 return setError(VBOX_E_OBJECT_NOT_FOUND,
4425 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4426 aDevice, aControllerPort, aControllerName);
4427
4428
4429 setModified(IsModified_Storage);
4430 mMediaData.backup();
4431
4432 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4433
4434 if (pAttach->getType() != DeviceType_DVD)
4435 return setError(E_INVALIDARG,
4436 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4437 aDevice, aControllerPort, aControllerName);
4438 pAttach->updateTempEject(!!aTemporaryEject);
4439
4440 return S_OK;
4441}
4442
4443STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4444 LONG aDevice, BOOL aNonRotational)
4445{
4446 CheckComArgStrNotEmptyOrNull(aControllerName);
4447
4448 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4449 aControllerName, aControllerPort, aDevice, aNonRotational));
4450
4451 AutoCaller autoCaller(this);
4452 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4453
4454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4455
4456 HRESULT rc = checkStateDependency(MutableStateDep);
4457 if (FAILED(rc)) return rc;
4458
4459 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4460
4461 if (Global::IsOnlineOrTransient(mData->mMachineState))
4462 return setError(VBOX_E_INVALID_VM_STATE,
4463 tr("Invalid machine state: %s"),
4464 Global::stringifyMachineState(mData->mMachineState));
4465
4466 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4467 aControllerName,
4468 aControllerPort,
4469 aDevice);
4470 if (!pAttach)
4471 return setError(VBOX_E_OBJECT_NOT_FOUND,
4472 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4473 aDevice, aControllerPort, aControllerName);
4474
4475
4476 setModified(IsModified_Storage);
4477 mMediaData.backup();
4478
4479 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4480
4481 if (pAttach->getType() != DeviceType_HardDisk)
4482 return setError(E_INVALIDARG,
4483 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"),
4484 aDevice, aControllerPort, aControllerName);
4485 pAttach->updateNonRotational(!!aNonRotational);
4486
4487 return S_OK;
4488}
4489
4490STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4491 LONG aDevice, BOOL aDiscard)
4492{
4493 CheckComArgStrNotEmptyOrNull(aControllerName);
4494
4495 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4496 aControllerName, aControllerPort, aDevice, aDiscard));
4497
4498 AutoCaller autoCaller(this);
4499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4500
4501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4502
4503 HRESULT rc = checkStateDependency(MutableStateDep);
4504 if (FAILED(rc)) return rc;
4505
4506 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4507
4508 if (Global::IsOnlineOrTransient(mData->mMachineState))
4509 return setError(VBOX_E_INVALID_VM_STATE,
4510 tr("Invalid machine state: %s"),
4511 Global::stringifyMachineState(mData->mMachineState));
4512
4513 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4514 aControllerName,
4515 aControllerPort,
4516 aDevice);
4517 if (!pAttach)
4518 return setError(VBOX_E_OBJECT_NOT_FOUND,
4519 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4520 aDevice, aControllerPort, aControllerName);
4521
4522
4523 setModified(IsModified_Storage);
4524 mMediaData.backup();
4525
4526 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4527
4528 if (pAttach->getType() != DeviceType_HardDisk)
4529 return setError(E_INVALIDARG,
4530 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"),
4531 aDevice, aControllerPort, aControllerName);
4532 pAttach->updateDiscard(!!aDiscard);
4533
4534 return S_OK;
4535}
4536
4537STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4538 LONG aDevice)
4539{
4540 int rc = S_OK;
4541 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4542 aControllerName, aControllerPort, aDevice));
4543
4544 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4545
4546 return rc;
4547}
4548
4549STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4550 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4551{
4552 CheckComArgStrNotEmptyOrNull(aControllerName);
4553
4554 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4555 aControllerName, aControllerPort, aDevice));
4556
4557 AutoCaller autoCaller(this);
4558 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4559
4560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4561
4562 HRESULT rc = checkStateDependency(MutableStateDep);
4563 if (FAILED(rc)) return rc;
4564
4565 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4566
4567 if (Global::IsOnlineOrTransient(mData->mMachineState))
4568 return setError(VBOX_E_INVALID_VM_STATE,
4569 tr("Invalid machine state: %s"),
4570 Global::stringifyMachineState(mData->mMachineState));
4571
4572 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4573 aControllerName,
4574 aControllerPort,
4575 aDevice);
4576 if (!pAttach)
4577 return setError(VBOX_E_OBJECT_NOT_FOUND,
4578 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4579 aDevice, aControllerPort, aControllerName);
4580
4581
4582 setModified(IsModified_Storage);
4583 mMediaData.backup();
4584
4585 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4586 if (aBandwidthGroup && group.isNull())
4587 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4588
4589 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4590
4591 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4592 if (strBandwidthGroupOld.isNotEmpty())
4593 {
4594 /* Get the bandwidth group object and release it - this must not fail. */
4595 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4596 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4597 Assert(SUCCEEDED(rc));
4598
4599 pBandwidthGroupOld->release();
4600 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4601 }
4602
4603 if (!group.isNull())
4604 {
4605 group->reference();
4606 pAttach->updateBandwidthGroup(group->getName());
4607 }
4608
4609 return S_OK;
4610}
4611
4612STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4613 LONG aControllerPort,
4614 LONG aDevice,
4615 DeviceType_T aType)
4616{
4617 HRESULT rc = S_OK;
4618
4619 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4620 aControllerName, aControllerPort, aDevice, aType));
4621
4622 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4623
4624 return rc;
4625}
4626
4627
4628
4629STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4630 LONG aControllerPort,
4631 LONG aDevice,
4632 BOOL aForce)
4633{
4634 int rc = S_OK;
4635 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4636 aControllerName, aControllerPort, aForce));
4637
4638 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4639
4640 return rc;
4641}
4642
4643STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4644 LONG aControllerPort,
4645 LONG aDevice,
4646 IMedium *aMedium,
4647 BOOL aForce)
4648{
4649 int rc = S_OK;
4650 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4651 aControllerName, aControllerPort, aDevice, aForce));
4652
4653 CheckComArgStrNotEmptyOrNull(aControllerName);
4654
4655 AutoCaller autoCaller(this);
4656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4657
4658 // request the host lock first, since might be calling Host methods for getting host drives;
4659 // next, protect the media tree all the while we're in here, as well as our member variables
4660 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4661 this->lockHandle(),
4662 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4663
4664 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4665 aControllerName,
4666 aControllerPort,
4667 aDevice);
4668 if (pAttach.isNull())
4669 return setError(VBOX_E_OBJECT_NOT_FOUND,
4670 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4671 aDevice, aControllerPort, aControllerName);
4672
4673 /* Remember previously mounted medium. The medium before taking the
4674 * backup is not necessarily the same thing. */
4675 ComObjPtr<Medium> oldmedium;
4676 oldmedium = pAttach->getMedium();
4677
4678 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4679 if (aMedium && pMedium.isNull())
4680 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4681
4682 AutoCaller mediumCaller(pMedium);
4683 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4684
4685 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4686 if (pMedium)
4687 {
4688 DeviceType_T mediumType = pAttach->getType();
4689 switch (mediumType)
4690 {
4691 case DeviceType_DVD:
4692 case DeviceType_Floppy:
4693 break;
4694
4695 default:
4696 return setError(VBOX_E_INVALID_OBJECT_STATE,
4697 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4698 aControllerPort,
4699 aDevice,
4700 aControllerName);
4701 }
4702 }
4703
4704 setModified(IsModified_Storage);
4705 mMediaData.backup();
4706
4707 {
4708 // The backup operation makes the pAttach reference point to the
4709 // old settings. Re-get the correct reference.
4710 pAttach = findAttachment(mMediaData->mAttachments,
4711 aControllerName,
4712 aControllerPort,
4713 aDevice);
4714 if (!oldmedium.isNull())
4715 oldmedium->removeBackReference(mData->mUuid);
4716 if (!pMedium.isNull())
4717 {
4718 pMedium->addBackReference(mData->mUuid);
4719
4720 mediumLock.release();
4721 multiLock.release();
4722 addMediumToRegistry(pMedium);
4723 multiLock.acquire();
4724 mediumLock.acquire();
4725 }
4726
4727 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4728 pAttach->updateMedium(pMedium);
4729 }
4730
4731 setModified(IsModified_Storage);
4732
4733 mediumLock.release();
4734 multiLock.release();
4735 rc = onMediumChange(pAttach, aForce);
4736 multiLock.acquire();
4737 mediumLock.acquire();
4738
4739 /* On error roll back this change only. */
4740 if (FAILED(rc))
4741 {
4742 if (!pMedium.isNull())
4743 pMedium->removeBackReference(mData->mUuid);
4744 pAttach = findAttachment(mMediaData->mAttachments,
4745 aControllerName,
4746 aControllerPort,
4747 aDevice);
4748 /* If the attachment is gone in the meantime, bail out. */
4749 if (pAttach.isNull())
4750 return rc;
4751 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4752 if (!oldmedium.isNull())
4753 oldmedium->addBackReference(mData->mUuid);
4754 pAttach->updateMedium(oldmedium);
4755 }
4756
4757 mediumLock.release();
4758 multiLock.release();
4759
4760 mParent->saveModifiedRegistries();
4761
4762 return rc;
4763}
4764
4765STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4766 LONG aControllerPort,
4767 LONG aDevice,
4768 IMedium **aMedium)
4769{
4770 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4771 aControllerName, aControllerPort, aDevice));
4772
4773 CheckComArgStrNotEmptyOrNull(aControllerName);
4774 CheckComArgOutPointerValid(aMedium);
4775
4776 AutoCaller autoCaller(this);
4777 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4778
4779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4780
4781 *aMedium = NULL;
4782
4783 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4784 aControllerName,
4785 aControllerPort,
4786 aDevice);
4787 if (pAttach.isNull())
4788 return setError(VBOX_E_OBJECT_NOT_FOUND,
4789 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4790 aDevice, aControllerPort, aControllerName);
4791
4792 pAttach->getMedium().queryInterfaceTo(aMedium);
4793
4794 return S_OK;
4795}
4796
4797STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4798{
4799 CheckComArgOutPointerValid(port);
4800 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4801
4802 AutoCaller autoCaller(this);
4803 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4804
4805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4806
4807 mSerialPorts[slot].queryInterfaceTo(port);
4808
4809 return S_OK;
4810}
4811
4812STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4813{
4814 CheckComArgOutPointerValid(port);
4815 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4816
4817 AutoCaller autoCaller(this);
4818 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4819
4820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4821
4822 mParallelPorts[slot].queryInterfaceTo(port);
4823
4824 return S_OK;
4825}
4826
4827STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4828{
4829 CheckComArgOutPointerValid(adapter);
4830 /* Do not assert if slot is out of range, just return the advertised
4831 status. testdriver/vbox.py triggers this in logVmInfo. */
4832 if (slot >= mNetworkAdapters.size())
4833 return setError(E_INVALIDARG,
4834 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4835 slot, mNetworkAdapters.size());
4836
4837 AutoCaller autoCaller(this);
4838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4839
4840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4841
4842 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4843
4844 return S_OK;
4845}
4846
4847STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4848{
4849 CheckComArgOutSafeArrayPointerValid(aKeys);
4850
4851 AutoCaller autoCaller(this);
4852 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4853
4854 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4855
4856 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4857 int i = 0;
4858 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4859 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4860 ++it, ++i)
4861 {
4862 const Utf8Str &strKey = it->first;
4863 strKey.cloneTo(&saKeys[i]);
4864 }
4865 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4866
4867 return S_OK;
4868 }
4869
4870 /**
4871 * @note Locks this object for reading.
4872 */
4873STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4874 BSTR *aValue)
4875{
4876 CheckComArgStrNotEmptyOrNull(aKey);
4877 CheckComArgOutPointerValid(aValue);
4878
4879 AutoCaller autoCaller(this);
4880 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4881
4882 /* start with nothing found */
4883 Bstr bstrResult("");
4884
4885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4886
4887 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4888 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4889 // found:
4890 bstrResult = it->second; // source is a Utf8Str
4891
4892 /* return the result to caller (may be empty) */
4893 bstrResult.cloneTo(aValue);
4894
4895 return S_OK;
4896}
4897
4898 /**
4899 * @note Locks mParent for writing + this object for writing.
4900 */
4901STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4902{
4903 CheckComArgStrNotEmptyOrNull(aKey);
4904
4905 AutoCaller autoCaller(this);
4906 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4907
4908 Utf8Str strKey(aKey);
4909 Utf8Str strValue(aValue);
4910 Utf8Str strOldValue; // empty
4911
4912 // locking note: we only hold the read lock briefly to look up the old value,
4913 // then release it and call the onExtraCanChange callbacks. There is a small
4914 // chance of a race insofar as the callback might be called twice if two callers
4915 // change the same key at the same time, but that's a much better solution
4916 // than the deadlock we had here before. The actual changing of the extradata
4917 // is then performed under the write lock and race-free.
4918
4919 // look up the old value first; if nothing has changed then we need not do anything
4920 {
4921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4922 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4923 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4924 strOldValue = it->second;
4925 }
4926
4927 bool fChanged;
4928 if ((fChanged = (strOldValue != strValue)))
4929 {
4930 // ask for permission from all listeners outside the locks;
4931 // onExtraDataCanChange() only briefly requests the VirtualBox
4932 // lock to copy the list of callbacks to invoke
4933 Bstr error;
4934 Bstr bstrValue(aValue);
4935
4936 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4937 {
4938 const char *sep = error.isEmpty() ? "" : ": ";
4939 CBSTR err = error.raw();
4940 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4941 sep, err));
4942 return setError(E_ACCESSDENIED,
4943 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4944 aKey,
4945 bstrValue.raw(),
4946 sep,
4947 err);
4948 }
4949
4950 // data is changing and change not vetoed: then write it out under the lock
4951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4952
4953 if (isSnapshotMachine())
4954 {
4955 HRESULT rc = checkStateDependency(MutableStateDep);
4956 if (FAILED(rc)) return rc;
4957 }
4958
4959 if (strValue.isEmpty())
4960 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
4961 else
4962 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
4963 // creates a new key if needed
4964
4965 bool fNeedsGlobalSaveSettings = false;
4966 saveSettings(&fNeedsGlobalSaveSettings);
4967
4968 if (fNeedsGlobalSaveSettings)
4969 {
4970 // save the global settings; for that we should hold only the VirtualBox lock
4971 alock.release();
4972 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4973 mParent->saveSettings();
4974 }
4975 }
4976
4977 // fire notification outside the lock
4978 if (fChanged)
4979 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
4980
4981 return S_OK;
4982}
4983
4984STDMETHODIMP Machine::SaveSettings()
4985{
4986 AutoCaller autoCaller(this);
4987 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4988
4989 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4990
4991 /* when there was auto-conversion, we want to save the file even if
4992 * the VM is saved */
4993 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
4994 if (FAILED(rc)) return rc;
4995
4996 /* the settings file path may never be null */
4997 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4998
4999 /* save all VM data excluding snapshots */
5000 bool fNeedsGlobalSaveSettings = false;
5001 rc = saveSettings(&fNeedsGlobalSaveSettings);
5002 mlock.release();
5003
5004 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5005 {
5006 // save the global settings; for that we should hold only the VirtualBox lock
5007 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5008 rc = mParent->saveSettings();
5009 }
5010
5011 return rc;
5012}
5013
5014STDMETHODIMP Machine::DiscardSettings()
5015{
5016 AutoCaller autoCaller(this);
5017 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5018
5019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5020
5021 HRESULT rc = checkStateDependency(MutableStateDep);
5022 if (FAILED(rc)) return rc;
5023
5024 /*
5025 * during this rollback, the session will be notified if data has
5026 * been actually changed
5027 */
5028 rollback(true /* aNotify */);
5029
5030 return S_OK;
5031}
5032
5033/** @note Locks objects! */
5034STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5035 ComSafeArrayOut(IMedium*, aMedia))
5036{
5037 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5038 AutoLimitedCaller autoCaller(this);
5039 AssertComRCReturnRC(autoCaller.rc());
5040
5041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5042
5043 Guid id(getId());
5044
5045 if (mData->mSession.mState != SessionState_Unlocked)
5046 return setError(VBOX_E_INVALID_OBJECT_STATE,
5047 tr("Cannot unregister the machine '%s' while it is locked"),
5048 mUserData->s.strName.c_str());
5049
5050 // wait for state dependents to drop to zero
5051 ensureNoStateDependencies();
5052
5053 if (!mData->mAccessible)
5054 {
5055 // inaccessible maschines can only be unregistered; uninitialize ourselves
5056 // here because currently there may be no unregistered that are inaccessible
5057 // (this state combination is not supported). Note releasing the caller and
5058 // leaving the lock before calling uninit()
5059 alock.release();
5060 autoCaller.release();
5061
5062 uninit();
5063
5064 mParent->unregisterMachine(this, id);
5065 // calls VirtualBox::saveSettings()
5066
5067 return S_OK;
5068 }
5069
5070 HRESULT rc = S_OK;
5071
5072 // discard saved state
5073 if (mData->mMachineState == MachineState_Saved)
5074 {
5075 // add the saved state file to the list of files the caller should delete
5076 Assert(!mSSData->strStateFilePath.isEmpty());
5077 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5078
5079 mSSData->strStateFilePath.setNull();
5080
5081 // unconditionally set the machine state to powered off, we now
5082 // know no session has locked the machine
5083 mData->mMachineState = MachineState_PoweredOff;
5084 }
5085
5086 size_t cSnapshots = 0;
5087 if (mData->mFirstSnapshot)
5088 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5089 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5090 // fail now before we start detaching media
5091 return setError(VBOX_E_INVALID_OBJECT_STATE,
5092 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5093 mUserData->s.strName.c_str(), cSnapshots);
5094
5095 // This list collects the medium objects from all medium attachments
5096 // which we will detach from the machine and its snapshots, in a specific
5097 // order which allows for closing all media without getting "media in use"
5098 // errors, simply by going through the list from the front to the back:
5099 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5100 // and must be closed before the parent media from the snapshots, or closing the parents
5101 // will fail because they still have children);
5102 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5103 // the root ("first") snapshot of the machine.
5104 MediaList llMedia;
5105
5106 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5107 && mMediaData->mAttachments.size()
5108 )
5109 {
5110 // we have media attachments: detach them all and add the Medium objects to our list
5111 if (cleanupMode != CleanupMode_UnregisterOnly)
5112 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5113 else
5114 return setError(VBOX_E_INVALID_OBJECT_STATE,
5115 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5116 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5117 }
5118
5119 if (cSnapshots)
5120 {
5121 // autoCleanup must be true here, or we would have failed above
5122
5123 // add the media from the medium attachments of the snapshots to llMedia
5124 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5125 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5126 // into the children first
5127
5128 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5129 MachineState_T oldState = mData->mMachineState;
5130 mData->mMachineState = MachineState_DeletingSnapshot;
5131
5132 // make a copy of the first snapshot so the refcount does not drop to 0
5133 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5134 // because of the AutoCaller voodoo)
5135 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5136
5137 // GO!
5138 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5139
5140 mData->mMachineState = oldState;
5141 }
5142
5143 if (FAILED(rc))
5144 {
5145 rollbackMedia();
5146 return rc;
5147 }
5148
5149 // commit all the media changes made above
5150 commitMedia();
5151
5152 mData->mRegistered = false;
5153
5154 // machine lock no longer needed
5155 alock.release();
5156
5157 // return media to caller
5158 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5159 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5160
5161 mParent->unregisterMachine(this, id);
5162 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5163
5164 return S_OK;
5165}
5166
5167struct Machine::DeleteTask
5168{
5169 ComObjPtr<Machine> pMachine;
5170 RTCList<ComPtr<IMedium> > llMediums;
5171 StringsList llFilesToDelete;
5172 ComObjPtr<Progress> pProgress;
5173};
5174
5175STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5176{
5177 LogFlowFuncEnter();
5178
5179 AutoCaller autoCaller(this);
5180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5181
5182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5183
5184 HRESULT rc = checkStateDependency(MutableStateDep);
5185 if (FAILED(rc)) return rc;
5186
5187 if (mData->mRegistered)
5188 return setError(VBOX_E_INVALID_VM_STATE,
5189 tr("Cannot delete settings of a registered machine"));
5190
5191 DeleteTask *pTask = new DeleteTask;
5192 pTask->pMachine = this;
5193 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5194
5195 // collect files to delete
5196 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5197
5198 for (size_t i = 0; i < sfaMedia.size(); ++i)
5199 {
5200 IMedium *pIMedium(sfaMedia[i]);
5201 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5202 if (pMedium.isNull())
5203 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5204 SafeArray<BSTR> ids;
5205 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5206 if (FAILED(rc)) return rc;
5207 /* At this point the medium should not have any back references
5208 * anymore. If it has it is attached to another VM and *must* not
5209 * deleted. */
5210 if (ids.size() < 1)
5211 pTask->llMediums.append(pMedium);
5212 }
5213 if (mData->pMachineConfigFile->fileExists())
5214 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5215
5216 pTask->pProgress.createObject();
5217 pTask->pProgress->init(getVirtualBox(),
5218 static_cast<IMachine*>(this) /* aInitiator */,
5219 Bstr(tr("Deleting files")).raw(),
5220 true /* fCancellable */,
5221 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5222 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5223
5224 int vrc = RTThreadCreate(NULL,
5225 Machine::deleteThread,
5226 (void*)pTask,
5227 0,
5228 RTTHREADTYPE_MAIN_WORKER,
5229 0,
5230 "MachineDelete");
5231
5232 pTask->pProgress.queryInterfaceTo(aProgress);
5233
5234 if (RT_FAILURE(vrc))
5235 {
5236 delete pTask;
5237 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5238 }
5239
5240 LogFlowFuncLeave();
5241
5242 return S_OK;
5243}
5244
5245/**
5246 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5247 * calls Machine::deleteTaskWorker() on the actual machine object.
5248 * @param Thread
5249 * @param pvUser
5250 * @return
5251 */
5252/*static*/
5253DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5254{
5255 LogFlowFuncEnter();
5256
5257 DeleteTask *pTask = (DeleteTask*)pvUser;
5258 Assert(pTask);
5259 Assert(pTask->pMachine);
5260 Assert(pTask->pProgress);
5261
5262 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5263 pTask->pProgress->notifyComplete(rc);
5264
5265 delete pTask;
5266
5267 LogFlowFuncLeave();
5268
5269 NOREF(Thread);
5270
5271 return VINF_SUCCESS;
5272}
5273
5274/**
5275 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5276 * @param task
5277 * @return
5278 */
5279HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5280{
5281 AutoCaller autoCaller(this);
5282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5283
5284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5285
5286 HRESULT rc = S_OK;
5287
5288 try
5289 {
5290 ULONG uLogHistoryCount = 3;
5291 ComPtr<ISystemProperties> systemProperties;
5292 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5293 if (FAILED(rc)) throw rc;
5294
5295 if (!systemProperties.isNull())
5296 {
5297 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5298 if (FAILED(rc)) throw rc;
5299 }
5300
5301 MachineState_T oldState = mData->mMachineState;
5302 setMachineState(MachineState_SettingUp);
5303 alock.release();
5304 for (size_t i = 0; i < task.llMediums.size(); ++i)
5305 {
5306 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5307 {
5308 AutoCaller mac(pMedium);
5309 if (FAILED(mac.rc())) throw mac.rc();
5310 Utf8Str strLocation = pMedium->getLocationFull();
5311 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5312 if (FAILED(rc)) throw rc;
5313 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5314 }
5315 ComPtr<IProgress> pProgress2;
5316 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5317 if (FAILED(rc)) throw rc;
5318 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5319 if (FAILED(rc)) throw rc;
5320 /* Check the result of the asynchrony process. */
5321 LONG iRc;
5322 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5323 if (FAILED(rc)) throw rc;
5324 /* If the thread of the progress object has an error, then
5325 * retrieve the error info from there, or it'll be lost. */
5326 if (FAILED(iRc))
5327 throw setError(ProgressErrorInfo(pProgress2));
5328 }
5329 setMachineState(oldState);
5330 alock.acquire();
5331
5332 // delete the files pushed on the task list by Machine::Delete()
5333 // (this includes saved states of the machine and snapshots and
5334 // medium storage files from the IMedium list passed in, and the
5335 // machine XML file)
5336 StringsList::const_iterator it = task.llFilesToDelete.begin();
5337 while (it != task.llFilesToDelete.end())
5338 {
5339 const Utf8Str &strFile = *it;
5340 LogFunc(("Deleting file %s\n", strFile.c_str()));
5341 int vrc = RTFileDelete(strFile.c_str());
5342 if (RT_FAILURE(vrc))
5343 throw setError(VBOX_E_IPRT_ERROR,
5344 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5345
5346 ++it;
5347 if (it == task.llFilesToDelete.end())
5348 {
5349 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5350 if (FAILED(rc)) throw rc;
5351 break;
5352 }
5353
5354 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5355 if (FAILED(rc)) throw rc;
5356 }
5357
5358 /* delete the settings only when the file actually exists */
5359 if (mData->pMachineConfigFile->fileExists())
5360 {
5361 /* Delete any backup or uncommitted XML files. Ignore failures.
5362 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5363 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5364 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5365 RTFileDelete(otherXml.c_str());
5366 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5367 RTFileDelete(otherXml.c_str());
5368
5369 /* delete the Logs folder, nothing important should be left
5370 * there (we don't check for errors because the user might have
5371 * some private files there that we don't want to delete) */
5372 Utf8Str logFolder;
5373 getLogFolder(logFolder);
5374 Assert(logFolder.length());
5375 if (RTDirExists(logFolder.c_str()))
5376 {
5377 /* Delete all VBox.log[.N] files from the Logs folder
5378 * (this must be in sync with the rotation logic in
5379 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5380 * files that may have been created by the GUI. */
5381 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5382 logFolder.c_str(), RTPATH_DELIMITER);
5383 RTFileDelete(log.c_str());
5384 log = Utf8StrFmt("%s%cVBox.png",
5385 logFolder.c_str(), RTPATH_DELIMITER);
5386 RTFileDelete(log.c_str());
5387 for (int i = uLogHistoryCount; i > 0; i--)
5388 {
5389 log = Utf8StrFmt("%s%cVBox.log.%d",
5390 logFolder.c_str(), RTPATH_DELIMITER, i);
5391 RTFileDelete(log.c_str());
5392 log = Utf8StrFmt("%s%cVBox.png.%d",
5393 logFolder.c_str(), RTPATH_DELIMITER, i);
5394 RTFileDelete(log.c_str());
5395 }
5396
5397 RTDirRemove(logFolder.c_str());
5398 }
5399
5400 /* delete the Snapshots folder, nothing important should be left
5401 * there (we don't check for errors because the user might have
5402 * some private files there that we don't want to delete) */
5403 Utf8Str strFullSnapshotFolder;
5404 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5405 Assert(!strFullSnapshotFolder.isEmpty());
5406 if (RTDirExists(strFullSnapshotFolder.c_str()))
5407 RTDirRemove(strFullSnapshotFolder.c_str());
5408
5409 // delete the directory that contains the settings file, but only
5410 // if it matches the VM name
5411 Utf8Str settingsDir;
5412 if (isInOwnDir(&settingsDir))
5413 RTDirRemove(settingsDir.c_str());
5414 }
5415
5416 alock.release();
5417
5418 mParent->saveModifiedRegistries();
5419 }
5420 catch (HRESULT aRC) { rc = aRC; }
5421
5422 return rc;
5423}
5424
5425STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5426{
5427 CheckComArgOutPointerValid(aSnapshot);
5428
5429 AutoCaller autoCaller(this);
5430 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5431
5432 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5433
5434 ComObjPtr<Snapshot> pSnapshot;
5435 HRESULT rc;
5436
5437 if (!aNameOrId || !*aNameOrId)
5438 // null case (caller wants root snapshot): findSnapshotById() handles this
5439 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5440 else
5441 {
5442 Guid uuid(aNameOrId);
5443 if (uuid.isValid())
5444 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5445 else
5446 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5447 }
5448 pSnapshot.queryInterfaceTo(aSnapshot);
5449
5450 return rc;
5451}
5452
5453STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5454{
5455 CheckComArgStrNotEmptyOrNull(aName);
5456 CheckComArgStrNotEmptyOrNull(aHostPath);
5457
5458 AutoCaller autoCaller(this);
5459 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5460
5461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5462
5463 HRESULT rc = checkStateDependency(MutableStateDep);
5464 if (FAILED(rc)) return rc;
5465
5466 Utf8Str strName(aName);
5467
5468 ComObjPtr<SharedFolder> sharedFolder;
5469 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5470 if (SUCCEEDED(rc))
5471 return setError(VBOX_E_OBJECT_IN_USE,
5472 tr("Shared folder named '%s' already exists"),
5473 strName.c_str());
5474
5475 sharedFolder.createObject();
5476 rc = sharedFolder->init(getMachine(),
5477 strName,
5478 aHostPath,
5479 !!aWritable,
5480 !!aAutoMount,
5481 true /* fFailOnError */);
5482 if (FAILED(rc)) return rc;
5483
5484 setModified(IsModified_SharedFolders);
5485 mHWData.backup();
5486 mHWData->mSharedFolders.push_back(sharedFolder);
5487
5488 /* inform the direct session if any */
5489 alock.release();
5490 onSharedFolderChange();
5491
5492 return S_OK;
5493}
5494
5495STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5496{
5497 CheckComArgStrNotEmptyOrNull(aName);
5498
5499 AutoCaller autoCaller(this);
5500 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5501
5502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5503
5504 HRESULT rc = checkStateDependency(MutableStateDep);
5505 if (FAILED(rc)) return rc;
5506
5507 ComObjPtr<SharedFolder> sharedFolder;
5508 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5509 if (FAILED(rc)) return rc;
5510
5511 setModified(IsModified_SharedFolders);
5512 mHWData.backup();
5513 mHWData->mSharedFolders.remove(sharedFolder);
5514
5515 /* inform the direct session if any */
5516 alock.release();
5517 onSharedFolderChange();
5518
5519 return S_OK;
5520}
5521
5522STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5523{
5524 CheckComArgOutPointerValid(aCanShow);
5525
5526 /* start with No */
5527 *aCanShow = FALSE;
5528
5529 AutoCaller autoCaller(this);
5530 AssertComRCReturnRC(autoCaller.rc());
5531
5532 ComPtr<IInternalSessionControl> directControl;
5533 {
5534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5535
5536 if (mData->mSession.mState != SessionState_Locked)
5537 return setError(VBOX_E_INVALID_VM_STATE,
5538 tr("Machine is not locked for session (session state: %s)"),
5539 Global::stringifySessionState(mData->mSession.mState));
5540
5541 directControl = mData->mSession.mDirectControl;
5542 }
5543
5544 /* ignore calls made after #OnSessionEnd() is called */
5545 if (!directControl)
5546 return S_OK;
5547
5548 LONG64 dummy;
5549 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5550}
5551
5552STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5553{
5554 CheckComArgOutPointerValid(aWinId);
5555
5556 AutoCaller autoCaller(this);
5557 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5558
5559 ComPtr<IInternalSessionControl> directControl;
5560 {
5561 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5562
5563 if (mData->mSession.mState != SessionState_Locked)
5564 return setError(E_FAIL,
5565 tr("Machine is not locked for session (session state: %s)"),
5566 Global::stringifySessionState(mData->mSession.mState));
5567
5568 directControl = mData->mSession.mDirectControl;
5569 }
5570
5571 /* ignore calls made after #OnSessionEnd() is called */
5572 if (!directControl)
5573 return S_OK;
5574
5575 BOOL dummy;
5576 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5577}
5578
5579#ifdef VBOX_WITH_GUEST_PROPS
5580/**
5581 * Look up a guest property in VBoxSVC's internal structures.
5582 */
5583HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5584 BSTR *aValue,
5585 LONG64 *aTimestamp,
5586 BSTR *aFlags) const
5587{
5588 using namespace guestProp;
5589
5590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5591 Utf8Str strName(aName);
5592 HWData::GuestPropertyMap::const_iterator it =
5593 mHWData->mGuestProperties.find(strName);
5594
5595 if (it != mHWData->mGuestProperties.end())
5596 {
5597 char szFlags[MAX_FLAGS_LEN + 1];
5598 it->second.strValue.cloneTo(aValue);
5599 *aTimestamp = it->second.mTimestamp;
5600 writeFlags(it->second.mFlags, szFlags);
5601 Bstr(szFlags).cloneTo(aFlags);
5602 }
5603
5604 return S_OK;
5605}
5606
5607/**
5608 * Query the VM that a guest property belongs to for the property.
5609 * @returns E_ACCESSDENIED if the VM process is not available or not
5610 * currently handling queries and the lookup should then be done in
5611 * VBoxSVC.
5612 */
5613HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5614 BSTR *aValue,
5615 LONG64 *aTimestamp,
5616 BSTR *aFlags) const
5617{
5618 HRESULT rc;
5619 ComPtr<IInternalSessionControl> directControl;
5620 directControl = mData->mSession.mDirectControl;
5621
5622 /* fail if we were called after #OnSessionEnd() is called. This is a
5623 * silly race condition. */
5624
5625 if (!directControl)
5626 rc = E_ACCESSDENIED;
5627 else
5628 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5629 false /* isSetter */,
5630 aValue, aTimestamp, aFlags);
5631 return rc;
5632}
5633#endif // VBOX_WITH_GUEST_PROPS
5634
5635STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5636 BSTR *aValue,
5637 LONG64 *aTimestamp,
5638 BSTR *aFlags)
5639{
5640#ifndef VBOX_WITH_GUEST_PROPS
5641 ReturnComNotImplemented();
5642#else // VBOX_WITH_GUEST_PROPS
5643 CheckComArgStrNotEmptyOrNull(aName);
5644 CheckComArgOutPointerValid(aValue);
5645 CheckComArgOutPointerValid(aTimestamp);
5646 CheckComArgOutPointerValid(aFlags);
5647
5648 AutoCaller autoCaller(this);
5649 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5650
5651 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5652 if (rc == E_ACCESSDENIED)
5653 /* The VM is not running or the service is not (yet) accessible */
5654 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5655 return rc;
5656#endif // VBOX_WITH_GUEST_PROPS
5657}
5658
5659STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5660{
5661 LONG64 dummyTimestamp;
5662 Bstr dummyFlags;
5663 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5664}
5665
5666STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5667{
5668 Bstr dummyValue;
5669 Bstr dummyFlags;
5670 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5671}
5672
5673#ifdef VBOX_WITH_GUEST_PROPS
5674/**
5675 * Set a guest property in VBoxSVC's internal structures.
5676 */
5677HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5678 IN_BSTR aFlags)
5679{
5680 using namespace guestProp;
5681
5682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5683 HRESULT rc = S_OK;
5684 HWData::GuestProperty property;
5685 property.mFlags = NILFLAG;
5686
5687 rc = checkStateDependency(MutableStateDep);
5688 if (FAILED(rc)) return rc;
5689
5690 try
5691 {
5692 Utf8Str utf8Name(aName);
5693 Utf8Str utf8Flags(aFlags);
5694 uint32_t fFlags = NILFLAG;
5695 if ( (aFlags != NULL)
5696 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5697 )
5698 return setError(E_INVALIDARG,
5699 tr("Invalid guest property flag values: '%ls'"),
5700 aFlags);
5701
5702 HWData::GuestPropertyMap::iterator it =
5703 mHWData->mGuestProperties.find(utf8Name);
5704
5705 if (it == mHWData->mGuestProperties.end())
5706 {
5707 setModified(IsModified_MachineData);
5708 mHWData.backupEx();
5709
5710 RTTIMESPEC time;
5711 HWData::GuestProperty prop;
5712 prop.strValue = aValue;
5713 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5714 prop.mFlags = fFlags;
5715
5716 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5717 }
5718 else
5719 {
5720 if (it->second.mFlags & (RDONLYHOST))
5721 {
5722 rc = setError(E_ACCESSDENIED,
5723 tr("The property '%ls' cannot be changed by the host"),
5724 aName);
5725 }
5726 else
5727 {
5728 setModified(IsModified_MachineData);
5729 mHWData.backupEx();
5730
5731 /* The backupEx() operation invalidates our iterator,
5732 * so get a new one. */
5733 it = mHWData->mGuestProperties.find(utf8Name);
5734 Assert(it != mHWData->mGuestProperties.end());
5735
5736 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5737 {
5738 RTTIMESPEC time;
5739 it->second.strValue = aValue;
5740 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5741 if (aFlags != NULL)
5742 it->second.mFlags = fFlags;
5743 }
5744 else
5745 {
5746 mHWData->mGuestProperties.erase(it);
5747 }
5748 }
5749 }
5750
5751 if ( SUCCEEDED(rc)
5752 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5753 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5754 RTSTR_MAX,
5755 utf8Name.c_str(),
5756 RTSTR_MAX,
5757 NULL)
5758 )
5759 )
5760 {
5761 alock.release();
5762
5763 mParent->onGuestPropertyChange(mData->mUuid, aName,
5764 aValue ? aValue : Bstr("").raw(),
5765 aFlags ? aFlags : Bstr("").raw());
5766 }
5767 }
5768 catch (std::bad_alloc &)
5769 {
5770 rc = E_OUTOFMEMORY;
5771 }
5772
5773 return rc;
5774}
5775
5776/**
5777 * Set a property on the VM that that property belongs to.
5778 * @returns E_ACCESSDENIED if the VM process is not available or not
5779 * currently handling queries and the setting should then be done in
5780 * VBoxSVC.
5781 */
5782HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5783 IN_BSTR aFlags)
5784{
5785 HRESULT rc;
5786
5787 try
5788 {
5789 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5790
5791 BSTR dummy = NULL; /* will not be changed (setter) */
5792 LONG64 dummy64;
5793 if (!directControl)
5794 rc = E_ACCESSDENIED;
5795 else
5796 /** @todo Fix when adding DeleteGuestProperty(),
5797 see defect. */
5798 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5799 true /* isSetter */,
5800 &dummy, &dummy64, &dummy);
5801 }
5802 catch (std::bad_alloc &)
5803 {
5804 rc = E_OUTOFMEMORY;
5805 }
5806
5807 return rc;
5808}
5809#endif // VBOX_WITH_GUEST_PROPS
5810
5811STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5812 IN_BSTR aFlags)
5813{
5814#ifndef VBOX_WITH_GUEST_PROPS
5815 ReturnComNotImplemented();
5816#else // VBOX_WITH_GUEST_PROPS
5817 CheckComArgStrNotEmptyOrNull(aName);
5818 CheckComArgMaybeNull(aFlags);
5819 CheckComArgMaybeNull(aValue);
5820
5821 AutoCaller autoCaller(this);
5822 if (FAILED(autoCaller.rc()))
5823 return autoCaller.rc();
5824
5825 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5826 if (rc == E_ACCESSDENIED)
5827 /* The VM is not running or the service is not (yet) accessible */
5828 rc = setGuestPropertyToService(aName, aValue, aFlags);
5829 return rc;
5830#endif // VBOX_WITH_GUEST_PROPS
5831}
5832
5833STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5834{
5835 return SetGuestProperty(aName, aValue, NULL);
5836}
5837
5838STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5839{
5840 return SetGuestProperty(aName, NULL, NULL);
5841}
5842
5843#ifdef VBOX_WITH_GUEST_PROPS
5844/**
5845 * Enumerate the guest properties in VBoxSVC's internal structures.
5846 */
5847HRESULT Machine::enumerateGuestPropertiesInService
5848 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5849 ComSafeArrayOut(BSTR, aValues),
5850 ComSafeArrayOut(LONG64, aTimestamps),
5851 ComSafeArrayOut(BSTR, aFlags))
5852{
5853 using namespace guestProp;
5854
5855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5856 Utf8Str strPatterns(aPatterns);
5857
5858 HWData::GuestPropertyMap propMap;
5859
5860 /*
5861 * Look for matching patterns and build up a list.
5862 */
5863 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5864 while (it != mHWData->mGuestProperties.end())
5865 {
5866 if ( strPatterns.isEmpty()
5867 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5868 RTSTR_MAX,
5869 it->first.c_str(),
5870 RTSTR_MAX,
5871 NULL)
5872 )
5873 {
5874 propMap.insert(*it);
5875 }
5876
5877 it++;
5878 }
5879
5880 alock.release();
5881
5882 /*
5883 * And build up the arrays for returning the property information.
5884 */
5885 size_t cEntries = propMap.size();
5886 SafeArray<BSTR> names(cEntries);
5887 SafeArray<BSTR> values(cEntries);
5888 SafeArray<LONG64> timestamps(cEntries);
5889 SafeArray<BSTR> flags(cEntries);
5890 size_t iProp = 0;
5891
5892 it = propMap.begin();
5893 while (it != propMap.end())
5894 {
5895 char szFlags[MAX_FLAGS_LEN + 1];
5896 it->first.cloneTo(&names[iProp]);
5897 it->second.strValue.cloneTo(&values[iProp]);
5898 timestamps[iProp] = it->second.mTimestamp;
5899 writeFlags(it->second.mFlags, szFlags);
5900 Bstr(szFlags).cloneTo(&flags[iProp++]);
5901 it++;
5902 }
5903 names.detachTo(ComSafeArrayOutArg(aNames));
5904 values.detachTo(ComSafeArrayOutArg(aValues));
5905 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5906 flags.detachTo(ComSafeArrayOutArg(aFlags));
5907 return S_OK;
5908}
5909
5910/**
5911 * Enumerate the properties managed by a VM.
5912 * @returns E_ACCESSDENIED if the VM process is not available or not
5913 * currently handling queries and the setting should then be done in
5914 * VBoxSVC.
5915 */
5916HRESULT Machine::enumerateGuestPropertiesOnVM
5917 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5918 ComSafeArrayOut(BSTR, aValues),
5919 ComSafeArrayOut(LONG64, aTimestamps),
5920 ComSafeArrayOut(BSTR, aFlags))
5921{
5922 HRESULT rc;
5923 ComPtr<IInternalSessionControl> directControl;
5924 directControl = mData->mSession.mDirectControl;
5925
5926 if (!directControl)
5927 rc = E_ACCESSDENIED;
5928 else
5929 rc = directControl->EnumerateGuestProperties
5930 (aPatterns, ComSafeArrayOutArg(aNames),
5931 ComSafeArrayOutArg(aValues),
5932 ComSafeArrayOutArg(aTimestamps),
5933 ComSafeArrayOutArg(aFlags));
5934 return rc;
5935}
5936#endif // VBOX_WITH_GUEST_PROPS
5937
5938STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5939 ComSafeArrayOut(BSTR, aNames),
5940 ComSafeArrayOut(BSTR, aValues),
5941 ComSafeArrayOut(LONG64, aTimestamps),
5942 ComSafeArrayOut(BSTR, aFlags))
5943{
5944#ifndef VBOX_WITH_GUEST_PROPS
5945 ReturnComNotImplemented();
5946#else // VBOX_WITH_GUEST_PROPS
5947 CheckComArgMaybeNull(aPatterns);
5948 CheckComArgOutSafeArrayPointerValid(aNames);
5949 CheckComArgOutSafeArrayPointerValid(aValues);
5950 CheckComArgOutSafeArrayPointerValid(aTimestamps);
5951 CheckComArgOutSafeArrayPointerValid(aFlags);
5952
5953 AutoCaller autoCaller(this);
5954 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5955
5956 HRESULT rc = enumerateGuestPropertiesOnVM
5957 (aPatterns, ComSafeArrayOutArg(aNames),
5958 ComSafeArrayOutArg(aValues),
5959 ComSafeArrayOutArg(aTimestamps),
5960 ComSafeArrayOutArg(aFlags));
5961 if (rc == E_ACCESSDENIED)
5962 /* The VM is not running or the service is not (yet) accessible */
5963 rc = enumerateGuestPropertiesInService
5964 (aPatterns, ComSafeArrayOutArg(aNames),
5965 ComSafeArrayOutArg(aValues),
5966 ComSafeArrayOutArg(aTimestamps),
5967 ComSafeArrayOutArg(aFlags));
5968 return rc;
5969#endif // VBOX_WITH_GUEST_PROPS
5970}
5971
5972STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
5973 ComSafeArrayOut(IMediumAttachment*, aAttachments))
5974{
5975 MediaData::AttachmentList atts;
5976
5977 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
5978 if (FAILED(rc)) return rc;
5979
5980 SafeIfaceArray<IMediumAttachment> attachments(atts);
5981 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
5982
5983 return S_OK;
5984}
5985
5986STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
5987 LONG aControllerPort,
5988 LONG aDevice,
5989 IMediumAttachment **aAttachment)
5990{
5991 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
5992 aControllerName, aControllerPort, aDevice));
5993
5994 CheckComArgStrNotEmptyOrNull(aControllerName);
5995 CheckComArgOutPointerValid(aAttachment);
5996
5997 AutoCaller autoCaller(this);
5998 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5999
6000 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6001
6002 *aAttachment = NULL;
6003
6004 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6005 aControllerName,
6006 aControllerPort,
6007 aDevice);
6008 if (pAttach.isNull())
6009 return setError(VBOX_E_OBJECT_NOT_FOUND,
6010 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6011 aDevice, aControllerPort, aControllerName);
6012
6013 pAttach.queryInterfaceTo(aAttachment);
6014
6015 return S_OK;
6016}
6017
6018STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6019 StorageBus_T aConnectionType,
6020 IStorageController **controller)
6021{
6022 CheckComArgStrNotEmptyOrNull(aName);
6023
6024 if ( (aConnectionType <= StorageBus_Null)
6025 || (aConnectionType > StorageBus_SAS))
6026 return setError(E_INVALIDARG,
6027 tr("Invalid connection type: %d"),
6028 aConnectionType);
6029
6030 AutoCaller autoCaller(this);
6031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6032
6033 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6034
6035 HRESULT rc = checkStateDependency(MutableStateDep);
6036 if (FAILED(rc)) return rc;
6037
6038 /* try to find one with the name first. */
6039 ComObjPtr<StorageController> ctrl;
6040
6041 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6042 if (SUCCEEDED(rc))
6043 return setError(VBOX_E_OBJECT_IN_USE,
6044 tr("Storage controller named '%ls' already exists"),
6045 aName);
6046
6047 ctrl.createObject();
6048
6049 /* get a new instance number for the storage controller */
6050 ULONG ulInstance = 0;
6051 bool fBootable = true;
6052 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6053 it != mStorageControllers->end();
6054 ++it)
6055 {
6056 if ((*it)->getStorageBus() == aConnectionType)
6057 {
6058 ULONG ulCurInst = (*it)->getInstance();
6059
6060 if (ulCurInst >= ulInstance)
6061 ulInstance = ulCurInst + 1;
6062
6063 /* Only one controller of each type can be marked as bootable. */
6064 if ((*it)->getBootable())
6065 fBootable = false;
6066 }
6067 }
6068
6069 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6070 if (FAILED(rc)) return rc;
6071
6072 setModified(IsModified_Storage);
6073 mStorageControllers.backup();
6074 mStorageControllers->push_back(ctrl);
6075
6076 ctrl.queryInterfaceTo(controller);
6077
6078 /* inform the direct session if any */
6079 alock.release();
6080 onStorageControllerChange();
6081
6082 return S_OK;
6083}
6084
6085STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6086 IStorageController **aStorageController)
6087{
6088 CheckComArgStrNotEmptyOrNull(aName);
6089
6090 AutoCaller autoCaller(this);
6091 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6092
6093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6094
6095 ComObjPtr<StorageController> ctrl;
6096
6097 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6098 if (SUCCEEDED(rc))
6099 ctrl.queryInterfaceTo(aStorageController);
6100
6101 return rc;
6102}
6103
6104STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6105 IStorageController **aStorageController)
6106{
6107 AutoCaller autoCaller(this);
6108 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6109
6110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6111
6112 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6113 it != mStorageControllers->end();
6114 ++it)
6115 {
6116 if ((*it)->getInstance() == aInstance)
6117 {
6118 (*it).queryInterfaceTo(aStorageController);
6119 return S_OK;
6120 }
6121 }
6122
6123 return setError(VBOX_E_OBJECT_NOT_FOUND,
6124 tr("Could not find a storage controller with instance number '%lu'"),
6125 aInstance);
6126}
6127
6128STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6129{
6130 AutoCaller autoCaller(this);
6131 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6132
6133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6134
6135 HRESULT rc = checkStateDependency(MutableStateDep);
6136 if (FAILED(rc)) return rc;
6137
6138 ComObjPtr<StorageController> ctrl;
6139
6140 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6141 if (SUCCEEDED(rc))
6142 {
6143 /* Ensure that only one controller of each type is marked as bootable. */
6144 if (fBootable == TRUE)
6145 {
6146 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6147 it != mStorageControllers->end();
6148 ++it)
6149 {
6150 ComObjPtr<StorageController> aCtrl = (*it);
6151
6152 if ( (aCtrl->getName() != Utf8Str(aName))
6153 && aCtrl->getBootable() == TRUE
6154 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6155 && aCtrl->getControllerType() == ctrl->getControllerType())
6156 {
6157 aCtrl->setBootable(FALSE);
6158 break;
6159 }
6160 }
6161 }
6162
6163 if (SUCCEEDED(rc))
6164 {
6165 ctrl->setBootable(fBootable);
6166 setModified(IsModified_Storage);
6167 }
6168 }
6169
6170 if (SUCCEEDED(rc))
6171 {
6172 /* inform the direct session if any */
6173 alock.release();
6174 onStorageControllerChange();
6175 }
6176
6177 return rc;
6178}
6179
6180STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6181{
6182 CheckComArgStrNotEmptyOrNull(aName);
6183
6184 AutoCaller autoCaller(this);
6185 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6186
6187 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6188
6189 HRESULT rc = checkStateDependency(MutableStateDep);
6190 if (FAILED(rc)) return rc;
6191
6192 ComObjPtr<StorageController> ctrl;
6193 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6194 if (FAILED(rc)) return rc;
6195
6196 {
6197 /* find all attached devices to the appropriate storage controller and detach them all */
6198 // make a temporary list because detachDevice invalidates iterators into
6199 // mMediaData->mAttachments
6200 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6201
6202 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6203 it != llAttachments2.end();
6204 ++it)
6205 {
6206 MediumAttachment *pAttachTemp = *it;
6207
6208 AutoCaller localAutoCaller(pAttachTemp);
6209 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6210
6211 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6212
6213 if (pAttachTemp->getControllerName() == aName)
6214 {
6215 rc = detachDevice(pAttachTemp, alock, NULL);
6216 if (FAILED(rc)) return rc;
6217 }
6218 }
6219 }
6220
6221 /* We can remove it now. */
6222 setModified(IsModified_Storage);
6223 mStorageControllers.backup();
6224
6225 ctrl->unshare();
6226
6227 mStorageControllers->remove(ctrl);
6228
6229 /* inform the direct session if any */
6230 alock.release();
6231 onStorageControllerChange();
6232
6233 return S_OK;
6234}
6235
6236STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6237 ULONG *puOriginX,
6238 ULONG *puOriginY,
6239 ULONG *puWidth,
6240 ULONG *puHeight,
6241 BOOL *pfEnabled)
6242{
6243 LogFlowThisFunc(("\n"));
6244
6245 CheckComArgNotNull(puOriginX);
6246 CheckComArgNotNull(puOriginY);
6247 CheckComArgNotNull(puWidth);
6248 CheckComArgNotNull(puHeight);
6249 CheckComArgNotNull(pfEnabled);
6250
6251 uint32_t u32OriginX= 0;
6252 uint32_t u32OriginY= 0;
6253 uint32_t u32Width = 0;
6254 uint32_t u32Height = 0;
6255 uint16_t u16Flags = 0;
6256
6257 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6258 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6259 if (RT_FAILURE(vrc))
6260 {
6261#ifdef RT_OS_WINDOWS
6262 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6263 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6264 * So just assign fEnable to TRUE again.
6265 * The right fix would be to change GUI API wrappers to make sure that parameters
6266 * are changed only if API succeeds.
6267 */
6268 *pfEnabled = TRUE;
6269#endif
6270 return setError(VBOX_E_IPRT_ERROR,
6271 tr("Saved guest size is not available (%Rrc)"),
6272 vrc);
6273 }
6274
6275 *puOriginX = u32OriginX;
6276 *puOriginY = u32OriginY;
6277 *puWidth = u32Width;
6278 *puHeight = u32Height;
6279 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6280
6281 return S_OK;
6282}
6283
6284STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6285{
6286 LogFlowThisFunc(("\n"));
6287
6288 CheckComArgNotNull(aSize);
6289 CheckComArgNotNull(aWidth);
6290 CheckComArgNotNull(aHeight);
6291
6292 if (aScreenId != 0)
6293 return E_NOTIMPL;
6294
6295 AutoCaller autoCaller(this);
6296 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6297
6298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6299
6300 uint8_t *pu8Data = NULL;
6301 uint32_t cbData = 0;
6302 uint32_t u32Width = 0;
6303 uint32_t u32Height = 0;
6304
6305 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6306
6307 if (RT_FAILURE(vrc))
6308 return setError(VBOX_E_IPRT_ERROR,
6309 tr("Saved screenshot data is not available (%Rrc)"),
6310 vrc);
6311
6312 *aSize = cbData;
6313 *aWidth = u32Width;
6314 *aHeight = u32Height;
6315
6316 freeSavedDisplayScreenshot(pu8Data);
6317
6318 return S_OK;
6319}
6320
6321STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6322{
6323 LogFlowThisFunc(("\n"));
6324
6325 CheckComArgNotNull(aWidth);
6326 CheckComArgNotNull(aHeight);
6327 CheckComArgOutSafeArrayPointerValid(aData);
6328
6329 if (aScreenId != 0)
6330 return E_NOTIMPL;
6331
6332 AutoCaller autoCaller(this);
6333 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6334
6335 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6336
6337 uint8_t *pu8Data = NULL;
6338 uint32_t cbData = 0;
6339 uint32_t u32Width = 0;
6340 uint32_t u32Height = 0;
6341
6342 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6343
6344 if (RT_FAILURE(vrc))
6345 return setError(VBOX_E_IPRT_ERROR,
6346 tr("Saved screenshot data is not available (%Rrc)"),
6347 vrc);
6348
6349 *aWidth = u32Width;
6350 *aHeight = u32Height;
6351
6352 com::SafeArray<BYTE> bitmap(cbData);
6353 /* Convert pixels to format expected by the API caller. */
6354 if (aBGR)
6355 {
6356 /* [0] B, [1] G, [2] R, [3] A. */
6357 for (unsigned i = 0; i < cbData; i += 4)
6358 {
6359 bitmap[i] = pu8Data[i];
6360 bitmap[i + 1] = pu8Data[i + 1];
6361 bitmap[i + 2] = pu8Data[i + 2];
6362 bitmap[i + 3] = 0xff;
6363 }
6364 }
6365 else
6366 {
6367 /* [0] R, [1] G, [2] B, [3] A. */
6368 for (unsigned i = 0; i < cbData; i += 4)
6369 {
6370 bitmap[i] = pu8Data[i + 2];
6371 bitmap[i + 1] = pu8Data[i + 1];
6372 bitmap[i + 2] = pu8Data[i];
6373 bitmap[i + 3] = 0xff;
6374 }
6375 }
6376 bitmap.detachTo(ComSafeArrayOutArg(aData));
6377
6378 freeSavedDisplayScreenshot(pu8Data);
6379
6380 return S_OK;
6381}
6382
6383
6384STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6385{
6386 LogFlowThisFunc(("\n"));
6387
6388 CheckComArgNotNull(aWidth);
6389 CheckComArgNotNull(aHeight);
6390 CheckComArgOutSafeArrayPointerValid(aData);
6391
6392 if (aScreenId != 0)
6393 return E_NOTIMPL;
6394
6395 AutoCaller autoCaller(this);
6396 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6397
6398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6399
6400 uint8_t *pu8Data = NULL;
6401 uint32_t cbData = 0;
6402 uint32_t u32Width = 0;
6403 uint32_t u32Height = 0;
6404
6405 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6406
6407 if (RT_FAILURE(vrc))
6408 return setError(VBOX_E_IPRT_ERROR,
6409 tr("Saved screenshot data is not available (%Rrc)"),
6410 vrc);
6411
6412 *aWidth = u32Width;
6413 *aHeight = u32Height;
6414
6415 HRESULT rc = S_OK;
6416 uint8_t *pu8PNG = NULL;
6417 uint32_t cbPNG = 0;
6418 uint32_t cxPNG = 0;
6419 uint32_t cyPNG = 0;
6420
6421 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6422
6423 if (RT_SUCCESS(vrc))
6424 {
6425 com::SafeArray<BYTE> screenData(cbPNG);
6426 screenData.initFrom(pu8PNG, cbPNG);
6427 if (pu8PNG)
6428 RTMemFree(pu8PNG);
6429 screenData.detachTo(ComSafeArrayOutArg(aData));
6430 }
6431 else
6432 {
6433 if (pu8PNG)
6434 RTMemFree(pu8PNG);
6435 return setError(VBOX_E_IPRT_ERROR,
6436 tr("Could not convert screenshot to PNG (%Rrc)"),
6437 vrc);
6438 }
6439
6440 freeSavedDisplayScreenshot(pu8Data);
6441
6442 return rc;
6443}
6444
6445STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6446{
6447 LogFlowThisFunc(("\n"));
6448
6449 CheckComArgNotNull(aSize);
6450 CheckComArgNotNull(aWidth);
6451 CheckComArgNotNull(aHeight);
6452
6453 if (aScreenId != 0)
6454 return E_NOTIMPL;
6455
6456 AutoCaller autoCaller(this);
6457 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6458
6459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6460
6461 uint8_t *pu8Data = NULL;
6462 uint32_t cbData = 0;
6463 uint32_t u32Width = 0;
6464 uint32_t u32Height = 0;
6465
6466 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6467
6468 if (RT_FAILURE(vrc))
6469 return setError(VBOX_E_IPRT_ERROR,
6470 tr("Saved screenshot data is not available (%Rrc)"),
6471 vrc);
6472
6473 *aSize = cbData;
6474 *aWidth = u32Width;
6475 *aHeight = u32Height;
6476
6477 freeSavedDisplayScreenshot(pu8Data);
6478
6479 return S_OK;
6480}
6481
6482STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6483{
6484 LogFlowThisFunc(("\n"));
6485
6486 CheckComArgNotNull(aWidth);
6487 CheckComArgNotNull(aHeight);
6488 CheckComArgOutSafeArrayPointerValid(aData);
6489
6490 if (aScreenId != 0)
6491 return E_NOTIMPL;
6492
6493 AutoCaller autoCaller(this);
6494 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6495
6496 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6497
6498 uint8_t *pu8Data = NULL;
6499 uint32_t cbData = 0;
6500 uint32_t u32Width = 0;
6501 uint32_t u32Height = 0;
6502
6503 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6504
6505 if (RT_FAILURE(vrc))
6506 return setError(VBOX_E_IPRT_ERROR,
6507 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6508 vrc);
6509
6510 *aWidth = u32Width;
6511 *aHeight = u32Height;
6512
6513 com::SafeArray<BYTE> png(cbData);
6514 png.initFrom(pu8Data, cbData);
6515 png.detachTo(ComSafeArrayOutArg(aData));
6516
6517 freeSavedDisplayScreenshot(pu8Data);
6518
6519 return S_OK;
6520}
6521
6522STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6523{
6524 HRESULT rc = S_OK;
6525 LogFlowThisFunc(("\n"));
6526
6527 AutoCaller autoCaller(this);
6528 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6529
6530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 if (!mHWData->mCPUHotPlugEnabled)
6533 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6534
6535 if (aCpu >= mHWData->mCPUCount)
6536 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6537
6538 if (mHWData->mCPUAttached[aCpu])
6539 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6540
6541 alock.release();
6542 rc = onCPUChange(aCpu, false);
6543 alock.acquire();
6544 if (FAILED(rc)) return rc;
6545
6546 setModified(IsModified_MachineData);
6547 mHWData.backup();
6548 mHWData->mCPUAttached[aCpu] = true;
6549
6550 /* Save settings if online */
6551 if (Global::IsOnline(mData->mMachineState))
6552 saveSettings(NULL);
6553
6554 return S_OK;
6555}
6556
6557STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6558{
6559 HRESULT rc = S_OK;
6560 LogFlowThisFunc(("\n"));
6561
6562 AutoCaller autoCaller(this);
6563 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6564
6565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6566
6567 if (!mHWData->mCPUHotPlugEnabled)
6568 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6569
6570 if (aCpu >= SchemaDefs::MaxCPUCount)
6571 return setError(E_INVALIDARG,
6572 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6573 SchemaDefs::MaxCPUCount);
6574
6575 if (!mHWData->mCPUAttached[aCpu])
6576 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6577
6578 /* CPU 0 can't be detached */
6579 if (aCpu == 0)
6580 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6581
6582 alock.release();
6583 rc = onCPUChange(aCpu, true);
6584 alock.acquire();
6585 if (FAILED(rc)) return rc;
6586
6587 setModified(IsModified_MachineData);
6588 mHWData.backup();
6589 mHWData->mCPUAttached[aCpu] = false;
6590
6591 /* Save settings if online */
6592 if (Global::IsOnline(mData->mMachineState))
6593 saveSettings(NULL);
6594
6595 return S_OK;
6596}
6597
6598STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6599{
6600 LogFlowThisFunc(("\n"));
6601
6602 CheckComArgNotNull(aCpuAttached);
6603
6604 *aCpuAttached = false;
6605
6606 AutoCaller autoCaller(this);
6607 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6608
6609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6610
6611 /* If hotplug is enabled the CPU is always enabled. */
6612 if (!mHWData->mCPUHotPlugEnabled)
6613 {
6614 if (aCpu < mHWData->mCPUCount)
6615 *aCpuAttached = true;
6616 }
6617 else
6618 {
6619 if (aCpu < SchemaDefs::MaxCPUCount)
6620 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6621 }
6622
6623 return S_OK;
6624}
6625
6626STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6627{
6628 CheckComArgOutPointerValid(aName);
6629
6630 AutoCaller autoCaller(this);
6631 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6632
6633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6634
6635 Utf8Str log = queryLogFilename(aIdx);
6636 if (!RTFileExists(log.c_str()))
6637 log.setNull();
6638 log.cloneTo(aName);
6639
6640 return S_OK;
6641}
6642
6643STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6644{
6645 LogFlowThisFunc(("\n"));
6646 CheckComArgOutSafeArrayPointerValid(aData);
6647 if (aSize < 0)
6648 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6649
6650 AutoCaller autoCaller(this);
6651 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6652
6653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6654
6655 HRESULT rc = S_OK;
6656 Utf8Str log = queryLogFilename(aIdx);
6657
6658 /* do not unnecessarily hold the lock while doing something which does
6659 * not need the lock and potentially takes a long time. */
6660 alock.release();
6661
6662 /* Limit the chunk size to 32K for now, as that gives better performance
6663 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6664 * One byte expands to approx. 25 bytes of breathtaking XML. */
6665 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6666 com::SafeArray<BYTE> logData(cbData);
6667
6668 RTFILE LogFile;
6669 int vrc = RTFileOpen(&LogFile, log.c_str(),
6670 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6671 if (RT_SUCCESS(vrc))
6672 {
6673 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6674 if (RT_SUCCESS(vrc))
6675 logData.resize(cbData);
6676 else
6677 rc = setError(VBOX_E_IPRT_ERROR,
6678 tr("Could not read log file '%s' (%Rrc)"),
6679 log.c_str(), vrc);
6680 RTFileClose(LogFile);
6681 }
6682 else
6683 rc = setError(VBOX_E_IPRT_ERROR,
6684 tr("Could not open log file '%s' (%Rrc)"),
6685 log.c_str(), vrc);
6686
6687 if (FAILED(rc))
6688 logData.resize(0);
6689 logData.detachTo(ComSafeArrayOutArg(aData));
6690
6691 return rc;
6692}
6693
6694
6695/**
6696 * Currently this method doesn't attach device to the running VM,
6697 * just makes sure it's plugged on next VM start.
6698 */
6699STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6700{
6701 AutoCaller autoCaller(this);
6702 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6703
6704 // lock scope
6705 {
6706 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6707
6708 HRESULT rc = checkStateDependency(MutableStateDep);
6709 if (FAILED(rc)) return rc;
6710
6711 ChipsetType_T aChipset = ChipsetType_PIIX3;
6712 COMGETTER(ChipsetType)(&aChipset);
6713
6714 if (aChipset != ChipsetType_ICH9)
6715 {
6716 return setError(E_INVALIDARG,
6717 tr("Host PCI attachment only supported with ICH9 chipset"));
6718 }
6719
6720 // check if device with this host PCI address already attached
6721 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6722 it != mHWData->mPCIDeviceAssignments.end();
6723 ++it)
6724 {
6725 LONG iHostAddress = -1;
6726 ComPtr<PCIDeviceAttachment> pAttach;
6727 pAttach = *it;
6728 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6729 if (iHostAddress == hostAddress)
6730 return setError(E_INVALIDARG,
6731 tr("Device with host PCI address already attached to this VM"));
6732 }
6733
6734 ComObjPtr<PCIDeviceAttachment> pda;
6735 char name[32];
6736
6737 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6738 Bstr bname(name);
6739 pda.createObject();
6740 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6741 setModified(IsModified_MachineData);
6742 mHWData.backup();
6743 mHWData->mPCIDeviceAssignments.push_back(pda);
6744 }
6745
6746 return S_OK;
6747}
6748
6749/**
6750 * Currently this method doesn't detach device from the running VM,
6751 * just makes sure it's not plugged on next VM start.
6752 */
6753STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6754{
6755 AutoCaller autoCaller(this);
6756 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6757
6758 ComObjPtr<PCIDeviceAttachment> pAttach;
6759 bool fRemoved = false;
6760 HRESULT rc;
6761
6762 // lock scope
6763 {
6764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6765
6766 rc = checkStateDependency(MutableStateDep);
6767 if (FAILED(rc)) return rc;
6768
6769 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6770 it != mHWData->mPCIDeviceAssignments.end();
6771 ++it)
6772 {
6773 LONG iHostAddress = -1;
6774 pAttach = *it;
6775 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6776 if (iHostAddress != -1 && iHostAddress == hostAddress)
6777 {
6778 setModified(IsModified_MachineData);
6779 mHWData.backup();
6780 mHWData->mPCIDeviceAssignments.remove(pAttach);
6781 fRemoved = true;
6782 break;
6783 }
6784 }
6785 }
6786
6787
6788 /* Fire event outside of the lock */
6789 if (fRemoved)
6790 {
6791 Assert(!pAttach.isNull());
6792 ComPtr<IEventSource> es;
6793 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6794 Assert(SUCCEEDED(rc));
6795 Bstr mid;
6796 rc = this->COMGETTER(Id)(mid.asOutParam());
6797 Assert(SUCCEEDED(rc));
6798 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6799 }
6800
6801 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6802 tr("No host PCI device %08x attached"),
6803 hostAddress
6804 );
6805}
6806
6807STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6808{
6809 CheckComArgOutSafeArrayPointerValid(aAssignments);
6810
6811 AutoCaller autoCaller(this);
6812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6813
6814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6815
6816 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6817 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6818
6819 return S_OK;
6820}
6821
6822STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6823{
6824 CheckComArgOutPointerValid(aBandwidthControl);
6825
6826 AutoCaller autoCaller(this);
6827 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6828
6829 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6830
6831 return S_OK;
6832}
6833
6834STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6835{
6836 CheckComArgOutPointerValid(pfEnabled);
6837 AutoCaller autoCaller(this);
6838 HRESULT hrc = autoCaller.rc();
6839 if (SUCCEEDED(hrc))
6840 {
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6843 }
6844 return hrc;
6845}
6846
6847STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6848{
6849 AutoCaller autoCaller(this);
6850 HRESULT hrc = autoCaller.rc();
6851 if (SUCCEEDED(hrc))
6852 {
6853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 hrc = checkStateDependency(MutableStateDep);
6855 if (SUCCEEDED(hrc))
6856 {
6857 hrc = mHWData.backupEx();
6858 if (SUCCEEDED(hrc))
6859 {
6860 setModified(IsModified_MachineData);
6861 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6862 }
6863 }
6864 }
6865 return hrc;
6866}
6867
6868STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6869{
6870 CheckComArgOutPointerValid(pbstrConfig);
6871 AutoCaller autoCaller(this);
6872 HRESULT hrc = autoCaller.rc();
6873 if (SUCCEEDED(hrc))
6874 {
6875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6876 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6877 }
6878 return hrc;
6879}
6880
6881STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6882{
6883 CheckComArgStr(bstrConfig);
6884 AutoCaller autoCaller(this);
6885 HRESULT hrc = autoCaller.rc();
6886 if (SUCCEEDED(hrc))
6887 {
6888 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6889 hrc = checkStateDependency(MutableStateDep);
6890 if (SUCCEEDED(hrc))
6891 {
6892 hrc = mHWData.backupEx();
6893 if (SUCCEEDED(hrc))
6894 {
6895 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6896 if (SUCCEEDED(hrc))
6897 setModified(IsModified_MachineData);
6898 }
6899 }
6900 }
6901 return hrc;
6902
6903}
6904
6905STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6906{
6907 CheckComArgOutPointerValid(pfAllow);
6908 AutoCaller autoCaller(this);
6909 HRESULT hrc = autoCaller.rc();
6910 if (SUCCEEDED(hrc))
6911 {
6912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6913 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6914 }
6915 return hrc;
6916}
6917
6918STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6919{
6920 AutoCaller autoCaller(this);
6921 HRESULT hrc = autoCaller.rc();
6922 if (SUCCEEDED(hrc))
6923 {
6924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6925 hrc = checkStateDependency(MutableStateDep);
6926 if (SUCCEEDED(hrc))
6927 {
6928 hrc = mHWData.backupEx();
6929 if (SUCCEEDED(hrc))
6930 {
6931 setModified(IsModified_MachineData);
6932 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6933 }
6934 }
6935 }
6936 return hrc;
6937}
6938
6939STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6940{
6941 CheckComArgOutPointerValid(pfEnabled);
6942 AutoCaller autoCaller(this);
6943 HRESULT hrc = autoCaller.rc();
6944 if (SUCCEEDED(hrc))
6945 {
6946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6947 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6948 }
6949 return hrc;
6950}
6951
6952STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
6953{
6954 AutoCaller autoCaller(this);
6955 HRESULT hrc = autoCaller.rc();
6956 if (SUCCEEDED(hrc))
6957 {
6958 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6959 hrc = checkStateDependency(MutableStateDep);
6960 if ( SUCCEEDED(hrc)
6961 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
6962 {
6963 AutostartDb *autostartDb = mParent->getAutostartDb();
6964 int vrc;
6965
6966 if (fEnabled)
6967 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6968 else
6969 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6970
6971 if (RT_SUCCESS(vrc))
6972 {
6973 hrc = mHWData.backupEx();
6974 if (SUCCEEDED(hrc))
6975 {
6976 setModified(IsModified_MachineData);
6977 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
6978 }
6979 }
6980 else if (vrc == VERR_NOT_SUPPORTED)
6981 hrc = setError(VBOX_E_NOT_SUPPORTED,
6982 tr("The VM autostart feature is not supported on this platform"));
6983 else if (vrc == VERR_PATH_NOT_FOUND)
6984 hrc = setError(E_FAIL,
6985 tr("The path to the autostart database is not set"));
6986 else
6987 hrc = setError(E_UNEXPECTED,
6988 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6989 fEnabled ? "Adding" : "Removing",
6990 mUserData->s.strName.c_str(), vrc);
6991 }
6992 }
6993 return hrc;
6994}
6995
6996STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
6997{
6998 CheckComArgOutPointerValid(puDelay);
6999 AutoCaller autoCaller(this);
7000 HRESULT hrc = autoCaller.rc();
7001 if (SUCCEEDED(hrc))
7002 {
7003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7004 *puDelay = mHWData->mAutostart.uAutostartDelay;
7005 }
7006 return hrc;
7007}
7008
7009STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7010{
7011 AutoCaller autoCaller(this);
7012 HRESULT hrc = autoCaller.rc();
7013 if (SUCCEEDED(hrc))
7014 {
7015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7016 hrc = checkStateDependency(MutableStateDep);
7017 if (SUCCEEDED(hrc))
7018 {
7019 hrc = mHWData.backupEx();
7020 if (SUCCEEDED(hrc))
7021 {
7022 setModified(IsModified_MachineData);
7023 mHWData->mAutostart.uAutostartDelay = uDelay;
7024 }
7025 }
7026 }
7027 return hrc;
7028}
7029
7030STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7031{
7032 CheckComArgOutPointerValid(penmAutostopType);
7033 AutoCaller autoCaller(this);
7034 HRESULT hrc = autoCaller.rc();
7035 if (SUCCEEDED(hrc))
7036 {
7037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7038 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7039 }
7040 return hrc;
7041}
7042
7043STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
7044{
7045 AutoCaller autoCaller(this);
7046 HRESULT hrc = autoCaller.rc();
7047 if (SUCCEEDED(hrc))
7048 {
7049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7050 hrc = checkStateDependency(MutableStateDep);
7051 if ( SUCCEEDED(hrc)
7052 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7053 {
7054 AutostartDb *autostartDb = mParent->getAutostartDb();
7055 int vrc;
7056
7057 if (enmAutostopType != AutostopType_Disabled)
7058 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7059 else
7060 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7061
7062 if (RT_SUCCESS(vrc))
7063 {
7064 hrc = mHWData.backupEx();
7065 if (SUCCEEDED(hrc))
7066 {
7067 setModified(IsModified_MachineData);
7068 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7069 }
7070 }
7071 else if (vrc == VERR_NOT_SUPPORTED)
7072 hrc = setError(VBOX_E_NOT_SUPPORTED,
7073 tr("The VM autostop feature is not supported on this platform"));
7074 else if (vrc == VERR_PATH_NOT_FOUND)
7075 hrc = setError(E_FAIL,
7076 tr("The path to the autostart database is not set"));
7077 else
7078 hrc = setError(E_UNEXPECTED,
7079 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7080 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7081 mUserData->s.strName.c_str(), vrc);
7082 }
7083 }
7084 return hrc;
7085}
7086
7087STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7088{
7089 CheckComArgOutPointerValid(aDefaultFrontend);
7090 AutoCaller autoCaller(this);
7091 HRESULT hrc = autoCaller.rc();
7092 if (SUCCEEDED(hrc))
7093 {
7094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7095 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7096 }
7097 return hrc;
7098}
7099
7100STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7101{
7102 CheckComArgStr(aDefaultFrontend);
7103 AutoCaller autoCaller(this);
7104 HRESULT hrc = autoCaller.rc();
7105 if (SUCCEEDED(hrc))
7106 {
7107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7108 hrc = checkStateDependency(MutableOrSavedStateDep);
7109 if (SUCCEEDED(hrc))
7110 {
7111 hrc = mHWData.backupEx();
7112 if (SUCCEEDED(hrc))
7113 {
7114 setModified(IsModified_MachineData);
7115 mHWData->mDefaultFrontend = aDefaultFrontend;
7116 }
7117 }
7118 }
7119 return hrc;
7120}
7121
7122
7123STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7124{
7125 LogFlowFuncEnter();
7126
7127 CheckComArgNotNull(pTarget);
7128 CheckComArgOutPointerValid(pProgress);
7129
7130 /* Convert the options. */
7131 RTCList<CloneOptions_T> optList;
7132 if (options != NULL)
7133 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7134
7135 if (optList.contains(CloneOptions_Link))
7136 {
7137 if (!isSnapshotMachine())
7138 return setError(E_INVALIDARG,
7139 tr("Linked clone can only be created from a snapshot"));
7140 if (mode != CloneMode_MachineState)
7141 return setError(E_INVALIDARG,
7142 tr("Linked clone can only be created for a single machine state"));
7143 }
7144 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7145
7146 AutoCaller autoCaller(this);
7147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7148
7149
7150 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7151
7152 HRESULT rc = pWorker->start(pProgress);
7153
7154 LogFlowFuncLeave();
7155
7156 return rc;
7157}
7158
7159// public methods for internal purposes
7160/////////////////////////////////////////////////////////////////////////////
7161
7162/**
7163 * Adds the given IsModified_* flag to the dirty flags of the machine.
7164 * This must be called either during loadSettings or under the machine write lock.
7165 * @param fl
7166 */
7167void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7168{
7169 mData->flModifications |= fl;
7170 if (fAllowStateModification && isStateModificationAllowed())
7171 mData->mCurrentStateModified = true;
7172}
7173
7174/**
7175 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7176 * care of the write locking.
7177 *
7178 * @param fModifications The flag to add.
7179 */
7180void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7181{
7182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7183 setModified(fModification, fAllowStateModification);
7184}
7185
7186/**
7187 * Saves the registry entry of this machine to the given configuration node.
7188 *
7189 * @param aEntryNode Node to save the registry entry to.
7190 *
7191 * @note locks this object for reading.
7192 */
7193HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7194{
7195 AutoLimitedCaller autoCaller(this);
7196 AssertComRCReturnRC(autoCaller.rc());
7197
7198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7199
7200 data.uuid = mData->mUuid;
7201 data.strSettingsFile = mData->m_strConfigFile;
7202
7203 return S_OK;
7204}
7205
7206/**
7207 * Calculates the absolute path of the given path taking the directory of the
7208 * machine settings file as the current directory.
7209 *
7210 * @param aPath Path to calculate the absolute path for.
7211 * @param aResult Where to put the result (used only on success, can be the
7212 * same Utf8Str instance as passed in @a aPath).
7213 * @return IPRT result.
7214 *
7215 * @note Locks this object for reading.
7216 */
7217int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7218{
7219 AutoCaller autoCaller(this);
7220 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7221
7222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7223
7224 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7225
7226 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7227
7228 strSettingsDir.stripFilename();
7229 char folder[RTPATH_MAX];
7230 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7231 if (RT_SUCCESS(vrc))
7232 aResult = folder;
7233
7234 return vrc;
7235}
7236
7237/**
7238 * Copies strSource to strTarget, making it relative to the machine folder
7239 * if it is a subdirectory thereof, or simply copying it otherwise.
7240 *
7241 * @param strSource Path to evaluate and copy.
7242 * @param strTarget Buffer to receive target path.
7243 *
7244 * @note Locks this object for reading.
7245 */
7246void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7247 Utf8Str &strTarget)
7248{
7249 AutoCaller autoCaller(this);
7250 AssertComRCReturn(autoCaller.rc(), (void)0);
7251
7252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7253
7254 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7255 // use strTarget as a temporary buffer to hold the machine settings dir
7256 strTarget = mData->m_strConfigFileFull;
7257 strTarget.stripFilename();
7258 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7259 {
7260 // is relative: then append what's left
7261 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7262 // for empty paths (only possible for subdirs) use "." to avoid
7263 // triggering default settings for not present config attributes.
7264 if (strTarget.isEmpty())
7265 strTarget = ".";
7266 }
7267 else
7268 // is not relative: then overwrite
7269 strTarget = strSource;
7270}
7271
7272/**
7273 * Returns the full path to the machine's log folder in the
7274 * \a aLogFolder argument.
7275 */
7276void Machine::getLogFolder(Utf8Str &aLogFolder)
7277{
7278 AutoCaller autoCaller(this);
7279 AssertComRCReturnVoid(autoCaller.rc());
7280
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 char szTmp[RTPATH_MAX];
7284 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7285 if (RT_SUCCESS(vrc))
7286 {
7287 if (szTmp[0] && !mUserData.isNull())
7288 {
7289 char szTmp2[RTPATH_MAX];
7290 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7291 if (RT_SUCCESS(vrc))
7292 aLogFolder = BstrFmt("%s%c%s",
7293 szTmp2,
7294 RTPATH_DELIMITER,
7295 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7296 }
7297 else
7298 vrc = VERR_PATH_IS_RELATIVE;
7299 }
7300
7301 if (RT_FAILURE(vrc))
7302 {
7303 // fallback if VBOX_USER_LOGHOME is not set or invalid
7304 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7305 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7306 aLogFolder.append(RTPATH_DELIMITER);
7307 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7308 }
7309}
7310
7311/**
7312 * Returns the full path to the machine's log file for an given index.
7313 */
7314Utf8Str Machine::queryLogFilename(ULONG idx)
7315{
7316 Utf8Str logFolder;
7317 getLogFolder(logFolder);
7318 Assert(logFolder.length());
7319 Utf8Str log;
7320 if (idx == 0)
7321 log = Utf8StrFmt("%s%cVBox.log",
7322 logFolder.c_str(), RTPATH_DELIMITER);
7323 else
7324 log = Utf8StrFmt("%s%cVBox.log.%d",
7325 logFolder.c_str(), RTPATH_DELIMITER, idx);
7326 return log;
7327}
7328
7329/**
7330 * Composes a unique saved state filename based on the current system time. The filename is
7331 * granular to the second so this will work so long as no more than one snapshot is taken on
7332 * a machine per second.
7333 *
7334 * Before version 4.1, we used this formula for saved state files:
7335 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7336 * which no longer works because saved state files can now be shared between the saved state of the
7337 * "saved" machine and an online snapshot, and the following would cause problems:
7338 * 1) save machine
7339 * 2) create online snapshot from that machine state --> reusing saved state file
7340 * 3) save machine again --> filename would be reused, breaking the online snapshot
7341 *
7342 * So instead we now use a timestamp.
7343 *
7344 * @param str
7345 */
7346void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7347{
7348 AutoCaller autoCaller(this);
7349 AssertComRCReturnVoid(autoCaller.rc());
7350
7351 {
7352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7353 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7354 }
7355
7356 RTTIMESPEC ts;
7357 RTTimeNow(&ts);
7358 RTTIME time;
7359 RTTimeExplode(&time, &ts);
7360
7361 strStateFilePath += RTPATH_DELIMITER;
7362 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7363 time.i32Year, time.u8Month, time.u8MonthDay,
7364 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7365}
7366
7367/**
7368 * @note Locks this object for writing, calls the client process
7369 * (inside the lock).
7370 */
7371HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7372 const Utf8Str &strFrontend,
7373 const Utf8Str &strEnvironment,
7374 ProgressProxy *aProgress)
7375{
7376 LogFlowThisFuncEnter();
7377
7378 AssertReturn(aControl, E_FAIL);
7379 AssertReturn(aProgress, E_FAIL);
7380
7381 AutoCaller autoCaller(this);
7382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7383
7384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7385
7386 if (!mData->mRegistered)
7387 return setError(E_UNEXPECTED,
7388 tr("The machine '%s' is not registered"),
7389 mUserData->s.strName.c_str());
7390
7391 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7392
7393 if ( mData->mSession.mState == SessionState_Locked
7394 || mData->mSession.mState == SessionState_Spawning
7395 || mData->mSession.mState == SessionState_Unlocking)
7396 return setError(VBOX_E_INVALID_OBJECT_STATE,
7397 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7398 mUserData->s.strName.c_str());
7399
7400 /* may not be busy */
7401 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7402
7403 /* get the path to the executable */
7404 char szPath[RTPATH_MAX];
7405 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7406 size_t sz = strlen(szPath);
7407 szPath[sz++] = RTPATH_DELIMITER;
7408 szPath[sz] = 0;
7409 char *cmd = szPath + sz;
7410 sz = RTPATH_MAX - sz;
7411
7412 int vrc = VINF_SUCCESS;
7413 RTPROCESS pid = NIL_RTPROCESS;
7414
7415 RTENV env = RTENV_DEFAULT;
7416
7417 if (!strEnvironment.isEmpty())
7418 {
7419 char *newEnvStr = NULL;
7420
7421 do
7422 {
7423 /* clone the current environment */
7424 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7425 AssertRCBreakStmt(vrc2, vrc = vrc2);
7426
7427 newEnvStr = RTStrDup(strEnvironment.c_str());
7428 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7429
7430 /* put new variables to the environment
7431 * (ignore empty variable names here since RTEnv API
7432 * intentionally doesn't do that) */
7433 char *var = newEnvStr;
7434 for (char *p = newEnvStr; *p; ++p)
7435 {
7436 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7437 {
7438 *p = '\0';
7439 if (*var)
7440 {
7441 char *val = strchr(var, '=');
7442 if (val)
7443 {
7444 *val++ = '\0';
7445 vrc2 = RTEnvSetEx(env, var, val);
7446 }
7447 else
7448 vrc2 = RTEnvUnsetEx(env, var);
7449 if (RT_FAILURE(vrc2))
7450 break;
7451 }
7452 var = p + 1;
7453 }
7454 }
7455 if (RT_SUCCESS(vrc2) && *var)
7456 vrc2 = RTEnvPutEx(env, var);
7457
7458 AssertRCBreakStmt(vrc2, vrc = vrc2);
7459 }
7460 while (0);
7461
7462 if (newEnvStr != NULL)
7463 RTStrFree(newEnvStr);
7464 }
7465
7466 /* Qt is default */
7467#ifdef VBOX_WITH_QTGUI
7468 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7469 {
7470# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7471 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7472# else
7473 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7474# endif
7475 Assert(sz >= sizeof(VirtualBox_exe));
7476 strcpy(cmd, VirtualBox_exe);
7477
7478 Utf8Str idStr = mData->mUuid.toString();
7479 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7480 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7481 }
7482#else /* !VBOX_WITH_QTGUI */
7483 if (0)
7484 ;
7485#endif /* VBOX_WITH_QTGUI */
7486
7487 else
7488
7489#ifdef VBOX_WITH_VBOXSDL
7490 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7491 {
7492 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7493 Assert(sz >= sizeof(VBoxSDL_exe));
7494 strcpy(cmd, VBoxSDL_exe);
7495
7496 Utf8Str idStr = mData->mUuid.toString();
7497 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7498 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7499 }
7500#else /* !VBOX_WITH_VBOXSDL */
7501 if (0)
7502 ;
7503#endif /* !VBOX_WITH_VBOXSDL */
7504
7505 else
7506
7507#ifdef VBOX_WITH_HEADLESS
7508 if ( strFrontend == "headless"
7509 || strFrontend == "capture"
7510 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7511 )
7512 {
7513 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7514 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7515 * and a VM works even if the server has not been installed.
7516 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7517 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7518 * differently in 4.0 and 3.x.
7519 */
7520 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7521 Assert(sz >= sizeof(VBoxHeadless_exe));
7522 strcpy(cmd, VBoxHeadless_exe);
7523
7524 Utf8Str idStr = mData->mUuid.toString();
7525 /* Leave space for "--capture" arg. */
7526 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7527 "--startvm", idStr.c_str(),
7528 "--vrde", "config",
7529 0, /* For "--capture". */
7530 0 };
7531 if (strFrontend == "capture")
7532 {
7533 unsigned pos = RT_ELEMENTS(args) - 2;
7534 args[pos] = "--capture";
7535 }
7536 vrc = RTProcCreate(szPath, args, env,
7537#ifdef RT_OS_WINDOWS
7538 RTPROC_FLAGS_NO_WINDOW
7539#else
7540 0
7541#endif
7542 , &pid);
7543 }
7544#else /* !VBOX_WITH_HEADLESS */
7545 if (0)
7546 ;
7547#endif /* !VBOX_WITH_HEADLESS */
7548 else
7549 {
7550 RTEnvDestroy(env);
7551 return setError(E_INVALIDARG,
7552 tr("Invalid frontend name: '%s'"),
7553 strFrontend.c_str());
7554 }
7555
7556 RTEnvDestroy(env);
7557
7558 if (RT_FAILURE(vrc))
7559 return setError(VBOX_E_IPRT_ERROR,
7560 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7561 mUserData->s.strName.c_str(), vrc);
7562
7563 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7564
7565 /*
7566 * Note that we don't release the lock here before calling the client,
7567 * because it doesn't need to call us back if called with a NULL argument.
7568 * Releasing the lock here is dangerous because we didn't prepare the
7569 * launch data yet, but the client we've just started may happen to be
7570 * too fast and call openSession() that will fail (because of PID, etc.),
7571 * so that the Machine will never get out of the Spawning session state.
7572 */
7573
7574 /* inform the session that it will be a remote one */
7575 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7576 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7577 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7578
7579 if (FAILED(rc))
7580 {
7581 /* restore the session state */
7582 mData->mSession.mState = SessionState_Unlocked;
7583 /* The failure may occur w/o any error info (from RPC), so provide one */
7584 return setError(VBOX_E_VM_ERROR,
7585 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7586 }
7587
7588 /* attach launch data to the machine */
7589 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7590 mData->mSession.mRemoteControls.push_back(aControl);
7591 mData->mSession.mProgress = aProgress;
7592 mData->mSession.mPID = pid;
7593 mData->mSession.mState = SessionState_Spawning;
7594 mData->mSession.mType = strFrontend;
7595
7596 LogFlowThisFuncLeave();
7597 return S_OK;
7598}
7599
7600/**
7601 * Returns @c true if the given machine has an open direct session and returns
7602 * the session machine instance and additional session data (on some platforms)
7603 * if so.
7604 *
7605 * Note that when the method returns @c false, the arguments remain unchanged.
7606 *
7607 * @param aMachine Session machine object.
7608 * @param aControl Direct session control object (optional).
7609 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7610 *
7611 * @note locks this object for reading.
7612 */
7613#if defined(RT_OS_WINDOWS)
7614bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7615 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7616 HANDLE *aIPCSem /*= NULL*/,
7617 bool aAllowClosing /*= false*/)
7618#elif defined(RT_OS_OS2)
7619bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7620 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7621 HMTX *aIPCSem /*= NULL*/,
7622 bool aAllowClosing /*= false*/)
7623#else
7624bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7625 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7626 bool aAllowClosing /*= false*/)
7627#endif
7628{
7629 AutoLimitedCaller autoCaller(this);
7630 AssertComRCReturn(autoCaller.rc(), false);
7631
7632 /* just return false for inaccessible machines */
7633 if (autoCaller.state() != Ready)
7634 return false;
7635
7636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7637
7638 if ( mData->mSession.mState == SessionState_Locked
7639 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7640 )
7641 {
7642 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7643
7644 aMachine = mData->mSession.mMachine;
7645
7646 if (aControl != NULL)
7647 *aControl = mData->mSession.mDirectControl;
7648
7649#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7650 /* Additional session data */
7651 if (aIPCSem != NULL)
7652 *aIPCSem = aMachine->mIPCSem;
7653#endif
7654 return true;
7655 }
7656
7657 return false;
7658}
7659
7660/**
7661 * Returns @c true if the given machine has an spawning direct session and
7662 * returns and additional session data (on some platforms) if so.
7663 *
7664 * Note that when the method returns @c false, the arguments remain unchanged.
7665 *
7666 * @param aPID PID of the spawned direct session process.
7667 *
7668 * @note locks this object for reading.
7669 */
7670#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7671bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7672#else
7673bool Machine::isSessionSpawning()
7674#endif
7675{
7676 AutoLimitedCaller autoCaller(this);
7677 AssertComRCReturn(autoCaller.rc(), false);
7678
7679 /* just return false for inaccessible machines */
7680 if (autoCaller.state() != Ready)
7681 return false;
7682
7683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7684
7685 if (mData->mSession.mState == SessionState_Spawning)
7686 {
7687#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7688 /* Additional session data */
7689 if (aPID != NULL)
7690 {
7691 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7692 *aPID = mData->mSession.mPID;
7693 }
7694#endif
7695 return true;
7696 }
7697
7698 return false;
7699}
7700
7701/**
7702 * Called from the client watcher thread to check for unexpected client process
7703 * death during Session_Spawning state (e.g. before it successfully opened a
7704 * direct session).
7705 *
7706 * On Win32 and on OS/2, this method is called only when we've got the
7707 * direct client's process termination notification, so it always returns @c
7708 * true.
7709 *
7710 * On other platforms, this method returns @c true if the client process is
7711 * terminated and @c false if it's still alive.
7712 *
7713 * @note Locks this object for writing.
7714 */
7715bool Machine::checkForSpawnFailure()
7716{
7717 AutoCaller autoCaller(this);
7718 if (!autoCaller.isOk())
7719 {
7720 /* nothing to do */
7721 LogFlowThisFunc(("Already uninitialized!\n"));
7722 return true;
7723 }
7724
7725 /* VirtualBox::addProcessToReap() needs a write lock */
7726 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7727
7728 if (mData->mSession.mState != SessionState_Spawning)
7729 {
7730 /* nothing to do */
7731 LogFlowThisFunc(("Not spawning any more!\n"));
7732 return true;
7733 }
7734
7735 HRESULT rc = S_OK;
7736
7737#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7738
7739 /* the process was already unexpectedly terminated, we just need to set an
7740 * error and finalize session spawning */
7741 rc = setError(E_FAIL,
7742 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7743 getName().c_str());
7744#else
7745
7746 /* PID not yet initialized, skip check. */
7747 if (mData->mSession.mPID == NIL_RTPROCESS)
7748 return false;
7749
7750 RTPROCSTATUS status;
7751 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7752 &status);
7753
7754 if (vrc != VERR_PROCESS_RUNNING)
7755 {
7756 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7757 rc = setError(E_FAIL,
7758 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7759 getName().c_str(), status.iStatus);
7760 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7761 rc = setError(E_FAIL,
7762 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7763 getName().c_str(), status.iStatus);
7764 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7765 rc = setError(E_FAIL,
7766 tr("The virtual machine '%s' has terminated abnormally"),
7767 getName().c_str(), status.iStatus);
7768 else
7769 rc = setError(E_FAIL,
7770 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7771 getName().c_str(), rc);
7772 }
7773
7774#endif
7775
7776 if (FAILED(rc))
7777 {
7778 /* Close the remote session, remove the remote control from the list
7779 * and reset session state to Closed (@note keep the code in sync with
7780 * the relevant part in checkForSpawnFailure()). */
7781
7782 Assert(mData->mSession.mRemoteControls.size() == 1);
7783 if (mData->mSession.mRemoteControls.size() == 1)
7784 {
7785 ErrorInfoKeeper eik;
7786 mData->mSession.mRemoteControls.front()->Uninitialize();
7787 }
7788
7789 mData->mSession.mRemoteControls.clear();
7790 mData->mSession.mState = SessionState_Unlocked;
7791
7792 /* finalize the progress after setting the state */
7793 if (!mData->mSession.mProgress.isNull())
7794 {
7795 mData->mSession.mProgress->notifyComplete(rc);
7796 mData->mSession.mProgress.setNull();
7797 }
7798
7799 mParent->addProcessToReap(mData->mSession.mPID);
7800 mData->mSession.mPID = NIL_RTPROCESS;
7801
7802 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7803 return true;
7804 }
7805
7806 return false;
7807}
7808
7809/**
7810 * Checks whether the machine can be registered. If so, commits and saves
7811 * all settings.
7812 *
7813 * @note Must be called from mParent's write lock. Locks this object and
7814 * children for writing.
7815 */
7816HRESULT Machine::prepareRegister()
7817{
7818 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7819
7820 AutoLimitedCaller autoCaller(this);
7821 AssertComRCReturnRC(autoCaller.rc());
7822
7823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7824
7825 /* wait for state dependents to drop to zero */
7826 ensureNoStateDependencies();
7827
7828 if (!mData->mAccessible)
7829 return setError(VBOX_E_INVALID_OBJECT_STATE,
7830 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7831 mUserData->s.strName.c_str(),
7832 mData->mUuid.toString().c_str());
7833
7834 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7835
7836 if (mData->mRegistered)
7837 return setError(VBOX_E_INVALID_OBJECT_STATE,
7838 tr("The machine '%s' with UUID {%s} is already registered"),
7839 mUserData->s.strName.c_str(),
7840 mData->mUuid.toString().c_str());
7841
7842 HRESULT rc = S_OK;
7843
7844 // Ensure the settings are saved. If we are going to be registered and
7845 // no config file exists yet, create it by calling saveSettings() too.
7846 if ( (mData->flModifications)
7847 || (!mData->pMachineConfigFile->fileExists())
7848 )
7849 {
7850 rc = saveSettings(NULL);
7851 // no need to check whether VirtualBox.xml needs saving too since
7852 // we can't have a machine XML file rename pending
7853 if (FAILED(rc)) return rc;
7854 }
7855
7856 /* more config checking goes here */
7857
7858 if (SUCCEEDED(rc))
7859 {
7860 /* we may have had implicit modifications we want to fix on success */
7861 commit();
7862
7863 mData->mRegistered = true;
7864 }
7865 else
7866 {
7867 /* we may have had implicit modifications we want to cancel on failure*/
7868 rollback(false /* aNotify */);
7869 }
7870
7871 return rc;
7872}
7873
7874/**
7875 * Increases the number of objects dependent on the machine state or on the
7876 * registered state. Guarantees that these two states will not change at least
7877 * until #releaseStateDependency() is called.
7878 *
7879 * Depending on the @a aDepType value, additional state checks may be made.
7880 * These checks will set extended error info on failure. See
7881 * #checkStateDependency() for more info.
7882 *
7883 * If this method returns a failure, the dependency is not added and the caller
7884 * is not allowed to rely on any particular machine state or registration state
7885 * value and may return the failed result code to the upper level.
7886 *
7887 * @param aDepType Dependency type to add.
7888 * @param aState Current machine state (NULL if not interested).
7889 * @param aRegistered Current registered state (NULL if not interested).
7890 *
7891 * @note Locks this object for writing.
7892 */
7893HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7894 MachineState_T *aState /* = NULL */,
7895 BOOL *aRegistered /* = NULL */)
7896{
7897 AutoCaller autoCaller(this);
7898 AssertComRCReturnRC(autoCaller.rc());
7899
7900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7901
7902 HRESULT rc = checkStateDependency(aDepType);
7903 if (FAILED(rc)) return rc;
7904
7905 {
7906 if (mData->mMachineStateChangePending != 0)
7907 {
7908 /* ensureNoStateDependencies() is waiting for state dependencies to
7909 * drop to zero so don't add more. It may make sense to wait a bit
7910 * and retry before reporting an error (since the pending state
7911 * transition should be really quick) but let's just assert for
7912 * now to see if it ever happens on practice. */
7913
7914 AssertFailed();
7915
7916 return setError(E_ACCESSDENIED,
7917 tr("Machine state change is in progress. Please retry the operation later."));
7918 }
7919
7920 ++mData->mMachineStateDeps;
7921 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7922 }
7923
7924 if (aState)
7925 *aState = mData->mMachineState;
7926 if (aRegistered)
7927 *aRegistered = mData->mRegistered;
7928
7929 return S_OK;
7930}
7931
7932/**
7933 * Decreases the number of objects dependent on the machine state.
7934 * Must always complete the #addStateDependency() call after the state
7935 * dependency is no more necessary.
7936 */
7937void Machine::releaseStateDependency()
7938{
7939 AutoCaller autoCaller(this);
7940 AssertComRCReturnVoid(autoCaller.rc());
7941
7942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7943
7944 /* releaseStateDependency() w/o addStateDependency()? */
7945 AssertReturnVoid(mData->mMachineStateDeps != 0);
7946 -- mData->mMachineStateDeps;
7947
7948 if (mData->mMachineStateDeps == 0)
7949 {
7950 /* inform ensureNoStateDependencies() that there are no more deps */
7951 if (mData->mMachineStateChangePending != 0)
7952 {
7953 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7954 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7955 }
7956 }
7957}
7958
7959Utf8Str Machine::getExtraData(const Utf8Str &strKey)
7960{
7961 /* start with nothing found */
7962 Utf8Str strResult("");
7963
7964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7965
7966 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7967 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7968 // found:
7969 strResult = it->second; // source is a Utf8Str
7970
7971 return strResult;
7972}
7973
7974// protected methods
7975/////////////////////////////////////////////////////////////////////////////
7976
7977/**
7978 * Performs machine state checks based on the @a aDepType value. If a check
7979 * fails, this method will set extended error info, otherwise it will return
7980 * S_OK. It is supposed, that on failure, the caller will immediately return
7981 * the return value of this method to the upper level.
7982 *
7983 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7984 *
7985 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7986 * current state of this machine object allows to change settings of the
7987 * machine (i.e. the machine is not registered, or registered but not running
7988 * and not saved). It is useful to call this method from Machine setters
7989 * before performing any change.
7990 *
7991 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7992 * as for MutableStateDep except that if the machine is saved, S_OK is also
7993 * returned. This is useful in setters which allow changing machine
7994 * properties when it is in the saved state.
7995 *
7996 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
7997 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
7998 * Aborted).
7999 *
8000 * @param aDepType Dependency type to check.
8001 *
8002 * @note Non Machine based classes should use #addStateDependency() and
8003 * #releaseStateDependency() methods or the smart AutoStateDependency
8004 * template.
8005 *
8006 * @note This method must be called from under this object's read or write
8007 * lock.
8008 */
8009HRESULT Machine::checkStateDependency(StateDependency aDepType)
8010{
8011 switch (aDepType)
8012 {
8013 case AnyStateDep:
8014 {
8015 break;
8016 }
8017 case MutableStateDep:
8018 {
8019 if ( mData->mRegistered
8020 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8021 || ( mData->mMachineState != MachineState_Paused
8022 && mData->mMachineState != MachineState_Running
8023 && mData->mMachineState != MachineState_Aborted
8024 && mData->mMachineState != MachineState_Teleported
8025 && mData->mMachineState != MachineState_PoweredOff
8026 )
8027 )
8028 )
8029 return setError(VBOX_E_INVALID_VM_STATE,
8030 tr("The machine is not mutable (state is %s)"),
8031 Global::stringifyMachineState(mData->mMachineState));
8032 break;
8033 }
8034 case MutableOrSavedStateDep:
8035 {
8036 if ( mData->mRegistered
8037 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8038 || ( mData->mMachineState != MachineState_Paused
8039 && mData->mMachineState != MachineState_Running
8040 && mData->mMachineState != MachineState_Aborted
8041 && mData->mMachineState != MachineState_Teleported
8042 && mData->mMachineState != MachineState_Saved
8043 && mData->mMachineState != MachineState_PoweredOff
8044 )
8045 )
8046 )
8047 return setError(VBOX_E_INVALID_VM_STATE,
8048 tr("The machine is not mutable (state is %s)"),
8049 Global::stringifyMachineState(mData->mMachineState));
8050 break;
8051 }
8052 case OfflineStateDep:
8053 {
8054 if ( mData->mRegistered
8055 && ( !isSessionMachine()
8056 || ( mData->mMachineState != MachineState_PoweredOff
8057 && mData->mMachineState != MachineState_Saved
8058 && mData->mMachineState != MachineState_Aborted
8059 && mData->mMachineState != MachineState_Teleported
8060 )
8061 )
8062 )
8063 return setError(VBOX_E_INVALID_VM_STATE,
8064 tr("The machine is not offline (state is %s)"),
8065 Global::stringifyMachineState(mData->mMachineState));
8066 break;
8067 }
8068 }
8069
8070 return S_OK;
8071}
8072
8073/**
8074 * Helper to initialize all associated child objects and allocate data
8075 * structures.
8076 *
8077 * This method must be called as a part of the object's initialization procedure
8078 * (usually done in the #init() method).
8079 *
8080 * @note Must be called only from #init() or from #registeredInit().
8081 */
8082HRESULT Machine::initDataAndChildObjects()
8083{
8084 AutoCaller autoCaller(this);
8085 AssertComRCReturnRC(autoCaller.rc());
8086 AssertComRCReturn(autoCaller.state() == InInit ||
8087 autoCaller.state() == Limited, E_FAIL);
8088
8089 AssertReturn(!mData->mAccessible, E_FAIL);
8090
8091 /* allocate data structures */
8092 mSSData.allocate();
8093 mUserData.allocate();
8094 mHWData.allocate();
8095 mMediaData.allocate();
8096 mStorageControllers.allocate();
8097
8098 /* initialize mOSTypeId */
8099 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8100
8101 /* create associated BIOS settings object */
8102 unconst(mBIOSSettings).createObject();
8103 mBIOSSettings->init(this);
8104
8105 /* create an associated VRDE object (default is disabled) */
8106 unconst(mVRDEServer).createObject();
8107 mVRDEServer->init(this);
8108
8109 /* create associated serial port objects */
8110 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8111 {
8112 unconst(mSerialPorts[slot]).createObject();
8113 mSerialPorts[slot]->init(this, slot);
8114 }
8115
8116 /* create associated parallel port objects */
8117 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8118 {
8119 unconst(mParallelPorts[slot]).createObject();
8120 mParallelPorts[slot]->init(this, slot);
8121 }
8122
8123 /* create the audio adapter object (always present, default is disabled) */
8124 unconst(mAudioAdapter).createObject();
8125 mAudioAdapter->init(this);
8126
8127 /* create the USB controller object (always present, default is disabled) */
8128 unconst(mUSBController).createObject();
8129 mUSBController->init(this);
8130
8131 /* create associated network adapter objects */
8132 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8133 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8134 {
8135 unconst(mNetworkAdapters[slot]).createObject();
8136 mNetworkAdapters[slot]->init(this, slot);
8137 }
8138
8139 /* create the bandwidth control */
8140 unconst(mBandwidthControl).createObject();
8141 mBandwidthControl->init(this);
8142
8143 return S_OK;
8144}
8145
8146/**
8147 * Helper to uninitialize all associated child objects and to free all data
8148 * structures.
8149 *
8150 * This method must be called as a part of the object's uninitialization
8151 * procedure (usually done in the #uninit() method).
8152 *
8153 * @note Must be called only from #uninit() or from #registeredInit().
8154 */
8155void Machine::uninitDataAndChildObjects()
8156{
8157 AutoCaller autoCaller(this);
8158 AssertComRCReturnVoid(autoCaller.rc());
8159 AssertComRCReturnVoid( autoCaller.state() == InUninit
8160 || autoCaller.state() == Limited);
8161
8162 /* tell all our other child objects we've been uninitialized */
8163 if (mBandwidthControl)
8164 {
8165 mBandwidthControl->uninit();
8166 unconst(mBandwidthControl).setNull();
8167 }
8168
8169 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8170 {
8171 if (mNetworkAdapters[slot])
8172 {
8173 mNetworkAdapters[slot]->uninit();
8174 unconst(mNetworkAdapters[slot]).setNull();
8175 }
8176 }
8177
8178 if (mUSBController)
8179 {
8180 mUSBController->uninit();
8181 unconst(mUSBController).setNull();
8182 }
8183
8184 if (mAudioAdapter)
8185 {
8186 mAudioAdapter->uninit();
8187 unconst(mAudioAdapter).setNull();
8188 }
8189
8190 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8191 {
8192 if (mParallelPorts[slot])
8193 {
8194 mParallelPorts[slot]->uninit();
8195 unconst(mParallelPorts[slot]).setNull();
8196 }
8197 }
8198
8199 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8200 {
8201 if (mSerialPorts[slot])
8202 {
8203 mSerialPorts[slot]->uninit();
8204 unconst(mSerialPorts[slot]).setNull();
8205 }
8206 }
8207
8208 if (mVRDEServer)
8209 {
8210 mVRDEServer->uninit();
8211 unconst(mVRDEServer).setNull();
8212 }
8213
8214 if (mBIOSSettings)
8215 {
8216 mBIOSSettings->uninit();
8217 unconst(mBIOSSettings).setNull();
8218 }
8219
8220 /* Deassociate media (only when a real Machine or a SnapshotMachine
8221 * instance is uninitialized; SessionMachine instances refer to real
8222 * Machine media). This is necessary for a clean re-initialization of
8223 * the VM after successfully re-checking the accessibility state. Note
8224 * that in case of normal Machine or SnapshotMachine uninitialization (as
8225 * a result of unregistering or deleting the snapshot), outdated media
8226 * attachments will already be uninitialized and deleted, so this
8227 * code will not affect them. */
8228 if ( !!mMediaData
8229 && (!isSessionMachine())
8230 )
8231 {
8232 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8233 it != mMediaData->mAttachments.end();
8234 ++it)
8235 {
8236 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8237 if (pMedium.isNull())
8238 continue;
8239 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8240 AssertComRC(rc);
8241 }
8242 }
8243
8244 if (!isSessionMachine() && !isSnapshotMachine())
8245 {
8246 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8247 if (mData->mFirstSnapshot)
8248 {
8249 // snapshots tree is protected by machine write lock; strictly
8250 // this isn't necessary here since we're deleting the entire
8251 // machine, but otherwise we assert in Snapshot::uninit()
8252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8253 mData->mFirstSnapshot->uninit();
8254 mData->mFirstSnapshot.setNull();
8255 }
8256
8257 mData->mCurrentSnapshot.setNull();
8258 }
8259
8260 /* free data structures (the essential mData structure is not freed here
8261 * since it may be still in use) */
8262 mMediaData.free();
8263 mStorageControllers.free();
8264 mHWData.free();
8265 mUserData.free();
8266 mSSData.free();
8267}
8268
8269/**
8270 * Returns a pointer to the Machine object for this machine that acts like a
8271 * parent for complex machine data objects such as shared folders, etc.
8272 *
8273 * For primary Machine objects and for SnapshotMachine objects, returns this
8274 * object's pointer itself. For SessionMachine objects, returns the peer
8275 * (primary) machine pointer.
8276 */
8277Machine* Machine::getMachine()
8278{
8279 if (isSessionMachine())
8280 return (Machine*)mPeer;
8281 return this;
8282}
8283
8284/**
8285 * Makes sure that there are no machine state dependents. If necessary, waits
8286 * for the number of dependents to drop to zero.
8287 *
8288 * Make sure this method is called from under this object's write lock to
8289 * guarantee that no new dependents may be added when this method returns
8290 * control to the caller.
8291 *
8292 * @note Locks this object for writing. The lock will be released while waiting
8293 * (if necessary).
8294 *
8295 * @warning To be used only in methods that change the machine state!
8296 */
8297void Machine::ensureNoStateDependencies()
8298{
8299 AssertReturnVoid(isWriteLockOnCurrentThread());
8300
8301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8302
8303 /* Wait for all state dependents if necessary */
8304 if (mData->mMachineStateDeps != 0)
8305 {
8306 /* lazy semaphore creation */
8307 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8308 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8309
8310 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8311 mData->mMachineStateDeps));
8312
8313 ++mData->mMachineStateChangePending;
8314
8315 /* reset the semaphore before waiting, the last dependent will signal
8316 * it */
8317 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8318
8319 alock.release();
8320
8321 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8322
8323 alock.acquire();
8324
8325 -- mData->mMachineStateChangePending;
8326 }
8327}
8328
8329/**
8330 * Changes the machine state and informs callbacks.
8331 *
8332 * This method is not intended to fail so it either returns S_OK or asserts (and
8333 * returns a failure).
8334 *
8335 * @note Locks this object for writing.
8336 */
8337HRESULT Machine::setMachineState(MachineState_T aMachineState)
8338{
8339 LogFlowThisFuncEnter();
8340 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8341
8342 AutoCaller autoCaller(this);
8343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8344
8345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8346
8347 /* wait for state dependents to drop to zero */
8348 ensureNoStateDependencies();
8349
8350 if (mData->mMachineState != aMachineState)
8351 {
8352 mData->mMachineState = aMachineState;
8353
8354 RTTimeNow(&mData->mLastStateChange);
8355
8356 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8357 }
8358
8359 LogFlowThisFuncLeave();
8360 return S_OK;
8361}
8362
8363/**
8364 * Searches for a shared folder with the given logical name
8365 * in the collection of shared folders.
8366 *
8367 * @param aName logical name of the shared folder
8368 * @param aSharedFolder where to return the found object
8369 * @param aSetError whether to set the error info if the folder is
8370 * not found
8371 * @return
8372 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8373 *
8374 * @note
8375 * must be called from under the object's lock!
8376 */
8377HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8378 ComObjPtr<SharedFolder> &aSharedFolder,
8379 bool aSetError /* = false */)
8380{
8381 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8382 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8383 it != mHWData->mSharedFolders.end();
8384 ++it)
8385 {
8386 SharedFolder *pSF = *it;
8387 AutoCaller autoCaller(pSF);
8388 if (pSF->getName() == aName)
8389 {
8390 aSharedFolder = pSF;
8391 rc = S_OK;
8392 break;
8393 }
8394 }
8395
8396 if (aSetError && FAILED(rc))
8397 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8398
8399 return rc;
8400}
8401
8402/**
8403 * Initializes all machine instance data from the given settings structures
8404 * from XML. The exception is the machine UUID which needs special handling
8405 * depending on the caller's use case, so the caller needs to set that herself.
8406 *
8407 * This gets called in several contexts during machine initialization:
8408 *
8409 * -- When machine XML exists on disk already and needs to be loaded into memory,
8410 * for example, from registeredInit() to load all registered machines on
8411 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8412 * attached to the machine should be part of some media registry already.
8413 *
8414 * -- During OVF import, when a machine config has been constructed from an
8415 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8416 * ensure that the media listed as attachments in the config (which have
8417 * been imported from the OVF) receive the correct registry ID.
8418 *
8419 * -- During VM cloning.
8420 *
8421 * @param config Machine settings from XML.
8422 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8423 * @return
8424 */
8425HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8426 const Guid *puuidRegistry)
8427{
8428 // copy name, description, OS type, teleporter, UTC etc.
8429 mUserData->s = config.machineUserData;
8430
8431 // look up the object by Id to check it is valid
8432 ComPtr<IGuestOSType> guestOSType;
8433 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8434 guestOSType.asOutParam());
8435 if (FAILED(rc)) return rc;
8436
8437 // stateFile (optional)
8438 if (config.strStateFile.isEmpty())
8439 mSSData->strStateFilePath.setNull();
8440 else
8441 {
8442 Utf8Str stateFilePathFull(config.strStateFile);
8443 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8444 if (RT_FAILURE(vrc))
8445 return setError(E_FAIL,
8446 tr("Invalid saved state file path '%s' (%Rrc)"),
8447 config.strStateFile.c_str(),
8448 vrc);
8449 mSSData->strStateFilePath = stateFilePathFull;
8450 }
8451
8452 // snapshot folder needs special processing so set it again
8453 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8454 if (FAILED(rc)) return rc;
8455
8456 /* Copy the extra data items (Not in any case config is already the same as
8457 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8458 * make sure the extra data map is copied). */
8459 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8460
8461 /* currentStateModified (optional, default is true) */
8462 mData->mCurrentStateModified = config.fCurrentStateModified;
8463
8464 mData->mLastStateChange = config.timeLastStateChange;
8465
8466 /*
8467 * note: all mUserData members must be assigned prior this point because
8468 * we need to commit changes in order to let mUserData be shared by all
8469 * snapshot machine instances.
8470 */
8471 mUserData.commitCopy();
8472
8473 // machine registry, if present (must be loaded before snapshots)
8474 if (config.canHaveOwnMediaRegistry())
8475 {
8476 // determine machine folder
8477 Utf8Str strMachineFolder = getSettingsFileFull();
8478 strMachineFolder.stripFilename();
8479 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8480 config.mediaRegistry,
8481 strMachineFolder);
8482 if (FAILED(rc)) return rc;
8483 }
8484
8485 /* Snapshot node (optional) */
8486 size_t cRootSnapshots;
8487 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8488 {
8489 // there must be only one root snapshot
8490 Assert(cRootSnapshots == 1);
8491
8492 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8493
8494 rc = loadSnapshot(snap,
8495 config.uuidCurrentSnapshot,
8496 NULL); // no parent == first snapshot
8497 if (FAILED(rc)) return rc;
8498 }
8499
8500 // hardware data
8501 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8502 if (FAILED(rc)) return rc;
8503
8504 // load storage controllers
8505 rc = loadStorageControllers(config.storageMachine,
8506 puuidRegistry,
8507 NULL /* puuidSnapshot */);
8508 if (FAILED(rc)) return rc;
8509
8510 /*
8511 * NOTE: the assignment below must be the last thing to do,
8512 * otherwise it will be not possible to change the settings
8513 * somewhere in the code above because all setters will be
8514 * blocked by checkStateDependency(MutableStateDep).
8515 */
8516
8517 /* set the machine state to Aborted or Saved when appropriate */
8518 if (config.fAborted)
8519 {
8520 mSSData->strStateFilePath.setNull();
8521
8522 /* no need to use setMachineState() during init() */
8523 mData->mMachineState = MachineState_Aborted;
8524 }
8525 else if (!mSSData->strStateFilePath.isEmpty())
8526 {
8527 /* no need to use setMachineState() during init() */
8528 mData->mMachineState = MachineState_Saved;
8529 }
8530
8531 // after loading settings, we are no longer different from the XML on disk
8532 mData->flModifications = 0;
8533
8534 return S_OK;
8535}
8536
8537/**
8538 * Recursively loads all snapshots starting from the given.
8539 *
8540 * @param aNode <Snapshot> node.
8541 * @param aCurSnapshotId Current snapshot ID from the settings file.
8542 * @param aParentSnapshot Parent snapshot.
8543 */
8544HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8545 const Guid &aCurSnapshotId,
8546 Snapshot *aParentSnapshot)
8547{
8548 AssertReturn(!isSnapshotMachine(), E_FAIL);
8549 AssertReturn(!isSessionMachine(), E_FAIL);
8550
8551 HRESULT rc = S_OK;
8552
8553 Utf8Str strStateFile;
8554 if (!data.strStateFile.isEmpty())
8555 {
8556 /* optional */
8557 strStateFile = data.strStateFile;
8558 int vrc = calculateFullPath(strStateFile, strStateFile);
8559 if (RT_FAILURE(vrc))
8560 return setError(E_FAIL,
8561 tr("Invalid saved state file path '%s' (%Rrc)"),
8562 strStateFile.c_str(),
8563 vrc);
8564 }
8565
8566 /* create a snapshot machine object */
8567 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8568 pSnapshotMachine.createObject();
8569 rc = pSnapshotMachine->initFromSettings(this,
8570 data.hardware,
8571 &data.debugging,
8572 &data.autostart,
8573 data.storage,
8574 data.uuid.ref(),
8575 strStateFile);
8576 if (FAILED(rc)) return rc;
8577
8578 /* create a snapshot object */
8579 ComObjPtr<Snapshot> pSnapshot;
8580 pSnapshot.createObject();
8581 /* initialize the snapshot */
8582 rc = pSnapshot->init(mParent, // VirtualBox object
8583 data.uuid,
8584 data.strName,
8585 data.strDescription,
8586 data.timestamp,
8587 pSnapshotMachine,
8588 aParentSnapshot);
8589 if (FAILED(rc)) return rc;
8590
8591 /* memorize the first snapshot if necessary */
8592 if (!mData->mFirstSnapshot)
8593 mData->mFirstSnapshot = pSnapshot;
8594
8595 /* memorize the current snapshot when appropriate */
8596 if ( !mData->mCurrentSnapshot
8597 && pSnapshot->getId() == aCurSnapshotId
8598 )
8599 mData->mCurrentSnapshot = pSnapshot;
8600
8601 // now create the children
8602 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8603 it != data.llChildSnapshots.end();
8604 ++it)
8605 {
8606 const settings::Snapshot &childData = *it;
8607 // recurse
8608 rc = loadSnapshot(childData,
8609 aCurSnapshotId,
8610 pSnapshot); // parent = the one we created above
8611 if (FAILED(rc)) return rc;
8612 }
8613
8614 return rc;
8615}
8616
8617/**
8618 * Loads settings into mHWData.
8619 *
8620 * @param data Reference to the hardware settings.
8621 * @param pDbg Pointer to the debugging settings.
8622 * @param pAutostart Pointer to the autostart settings.
8623 */
8624HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8625 const settings::Autostart *pAutostart)
8626{
8627 AssertReturn(!isSessionMachine(), E_FAIL);
8628
8629 HRESULT rc = S_OK;
8630
8631 try
8632 {
8633 /* The hardware version attribute (optional). */
8634 mHWData->mHWVersion = data.strVersion;
8635 mHWData->mHardwareUUID = data.uuid;
8636
8637 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8638 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8639 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8640 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8641 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8642 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8643 mHWData->mPAEEnabled = data.fPAE;
8644 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8645 mHWData->mLongMode = data.enmLongMode;
8646 mHWData->mCPUCount = data.cCPUs;
8647 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8648 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8649
8650 // cpu
8651 if (mHWData->mCPUHotPlugEnabled)
8652 {
8653 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8654 it != data.llCpus.end();
8655 ++it)
8656 {
8657 const settings::Cpu &cpu = *it;
8658
8659 mHWData->mCPUAttached[cpu.ulId] = true;
8660 }
8661 }
8662
8663 // cpuid leafs
8664 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8665 it != data.llCpuIdLeafs.end();
8666 ++it)
8667 {
8668 const settings::CpuIdLeaf &leaf = *it;
8669
8670 switch (leaf.ulId)
8671 {
8672 case 0x0:
8673 case 0x1:
8674 case 0x2:
8675 case 0x3:
8676 case 0x4:
8677 case 0x5:
8678 case 0x6:
8679 case 0x7:
8680 case 0x8:
8681 case 0x9:
8682 case 0xA:
8683 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8684 break;
8685
8686 case 0x80000000:
8687 case 0x80000001:
8688 case 0x80000002:
8689 case 0x80000003:
8690 case 0x80000004:
8691 case 0x80000005:
8692 case 0x80000006:
8693 case 0x80000007:
8694 case 0x80000008:
8695 case 0x80000009:
8696 case 0x8000000A:
8697 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8698 break;
8699
8700 default:
8701 /* just ignore */
8702 break;
8703 }
8704 }
8705
8706 mHWData->mMemorySize = data.ulMemorySizeMB;
8707 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8708
8709 // boot order
8710 for (size_t i = 0;
8711 i < RT_ELEMENTS(mHWData->mBootOrder);
8712 i++)
8713 {
8714 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8715 if (it == data.mapBootOrder.end())
8716 mHWData->mBootOrder[i] = DeviceType_Null;
8717 else
8718 mHWData->mBootOrder[i] = it->second;
8719 }
8720
8721 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8722 mHWData->mMonitorCount = data.cMonitors;
8723 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8724 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8725 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8726 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8727 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8728 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8729 mHWData->mFirmwareType = data.firmwareType;
8730 mHWData->mPointingHIDType = data.pointingHIDType;
8731 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8732 mHWData->mChipsetType = data.chipsetType;
8733 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8734 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8735 mHWData->mHPETEnabled = data.fHPETEnabled;
8736
8737 /* VRDEServer */
8738 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8739 if (FAILED(rc)) return rc;
8740
8741 /* BIOS */
8742 rc = mBIOSSettings->loadSettings(data.biosSettings);
8743 if (FAILED(rc)) return rc;
8744
8745 // Bandwidth control (must come before network adapters)
8746 rc = mBandwidthControl->loadSettings(data.ioSettings);
8747 if (FAILED(rc)) return rc;
8748
8749 /* USB Controller */
8750 rc = mUSBController->loadSettings(data.usbController);
8751 if (FAILED(rc)) return rc;
8752
8753 // network adapters
8754 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8755 uint32_t oldCount = mNetworkAdapters.size();
8756 if (newCount > oldCount)
8757 {
8758 mNetworkAdapters.resize(newCount);
8759 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8760 {
8761 unconst(mNetworkAdapters[slot]).createObject();
8762 mNetworkAdapters[slot]->init(this, slot);
8763 }
8764 }
8765 else if (newCount < oldCount)
8766 mNetworkAdapters.resize(newCount);
8767 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8768 it != data.llNetworkAdapters.end();
8769 ++it)
8770 {
8771 const settings::NetworkAdapter &nic = *it;
8772
8773 /* slot unicity is guaranteed by XML Schema */
8774 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8775 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8776 if (FAILED(rc)) return rc;
8777 }
8778
8779 // serial ports
8780 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8781 it != data.llSerialPorts.end();
8782 ++it)
8783 {
8784 const settings::SerialPort &s = *it;
8785
8786 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8787 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8788 if (FAILED(rc)) return rc;
8789 }
8790
8791 // parallel ports (optional)
8792 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8793 it != data.llParallelPorts.end();
8794 ++it)
8795 {
8796 const settings::ParallelPort &p = *it;
8797
8798 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8799 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8800 if (FAILED(rc)) return rc;
8801 }
8802
8803 /* AudioAdapter */
8804 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8805 if (FAILED(rc)) return rc;
8806
8807 /* Shared folders */
8808 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8809 it != data.llSharedFolders.end();
8810 ++it)
8811 {
8812 const settings::SharedFolder &sf = *it;
8813
8814 ComObjPtr<SharedFolder> sharedFolder;
8815 /* Check for double entries. Not allowed! */
8816 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8817 if (SUCCEEDED(rc))
8818 return setError(VBOX_E_OBJECT_IN_USE,
8819 tr("Shared folder named '%s' already exists"),
8820 sf.strName.c_str());
8821
8822 /* Create the new shared folder. Don't break on error. This will be
8823 * reported when the machine starts. */
8824 sharedFolder.createObject();
8825 rc = sharedFolder->init(getMachine(),
8826 sf.strName,
8827 sf.strHostPath,
8828 RT_BOOL(sf.fWritable),
8829 RT_BOOL(sf.fAutoMount),
8830 false /* fFailOnError */);
8831 if (FAILED(rc)) return rc;
8832 mHWData->mSharedFolders.push_back(sharedFolder);
8833 }
8834
8835 // Clipboard
8836 mHWData->mClipboardMode = data.clipboardMode;
8837
8838 // drag'n'drop
8839 mHWData->mDragAndDropMode = data.dragAndDropMode;
8840
8841 // guest settings
8842 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8843
8844 // IO settings
8845 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8846 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8847
8848 // Host PCI devices
8849 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8850 it != data.pciAttachments.end();
8851 ++it)
8852 {
8853 const settings::HostPCIDeviceAttachment &hpda = *it;
8854 ComObjPtr<PCIDeviceAttachment> pda;
8855
8856 pda.createObject();
8857 pda->loadSettings(this, hpda);
8858 mHWData->mPCIDeviceAssignments.push_back(pda);
8859 }
8860
8861 /*
8862 * (The following isn't really real hardware, but it lives in HWData
8863 * for reasons of convenience.)
8864 */
8865
8866#ifdef VBOX_WITH_GUEST_PROPS
8867 /* Guest properties (optional) */
8868 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8869 it != data.llGuestProperties.end();
8870 ++it)
8871 {
8872 const settings::GuestProperty &prop = *it;
8873 uint32_t fFlags = guestProp::NILFLAG;
8874 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8875 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8876 mHWData->mGuestProperties[prop.strName] = property;
8877 }
8878
8879 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8880#endif /* VBOX_WITH_GUEST_PROPS defined */
8881
8882 rc = loadDebugging(pDbg);
8883 if (FAILED(rc))
8884 return rc;
8885
8886 mHWData->mAutostart = *pAutostart;
8887
8888 /* default frontend */
8889 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8890 }
8891 catch(std::bad_alloc &)
8892 {
8893 return E_OUTOFMEMORY;
8894 }
8895
8896 AssertComRC(rc);
8897 return rc;
8898}
8899
8900/**
8901 * Called from Machine::loadHardware() to load the debugging settings of the
8902 * machine.
8903 *
8904 * @param pDbg Pointer to the settings.
8905 */
8906HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8907{
8908 mHWData->mDebugging = *pDbg;
8909 /* no more processing currently required, this will probably change. */
8910 return S_OK;
8911}
8912
8913/**
8914 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8915 *
8916 * @param data
8917 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8918 * @param puuidSnapshot
8919 * @return
8920 */
8921HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8922 const Guid *puuidRegistry,
8923 const Guid *puuidSnapshot)
8924{
8925 AssertReturn(!isSessionMachine(), E_FAIL);
8926
8927 HRESULT rc = S_OK;
8928
8929 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8930 it != data.llStorageControllers.end();
8931 ++it)
8932 {
8933 const settings::StorageController &ctlData = *it;
8934
8935 ComObjPtr<StorageController> pCtl;
8936 /* Try to find one with the name first. */
8937 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8938 if (SUCCEEDED(rc))
8939 return setError(VBOX_E_OBJECT_IN_USE,
8940 tr("Storage controller named '%s' already exists"),
8941 ctlData.strName.c_str());
8942
8943 pCtl.createObject();
8944 rc = pCtl->init(this,
8945 ctlData.strName,
8946 ctlData.storageBus,
8947 ctlData.ulInstance,
8948 ctlData.fBootable);
8949 if (FAILED(rc)) return rc;
8950
8951 mStorageControllers->push_back(pCtl);
8952
8953 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
8954 if (FAILED(rc)) return rc;
8955
8956 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
8957 if (FAILED(rc)) return rc;
8958
8959 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
8960 if (FAILED(rc)) return rc;
8961
8962 /* Set IDE emulation settings (only for AHCI controller). */
8963 if (ctlData.controllerType == StorageControllerType_IntelAhci)
8964 {
8965 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
8966 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
8967 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
8968 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
8969 )
8970 return rc;
8971 }
8972
8973 /* Load the attached devices now. */
8974 rc = loadStorageDevices(pCtl,
8975 ctlData,
8976 puuidRegistry,
8977 puuidSnapshot);
8978 if (FAILED(rc)) return rc;
8979 }
8980
8981 return S_OK;
8982}
8983
8984/**
8985 * Called from loadStorageControllers for a controller's devices.
8986 *
8987 * @param aStorageController
8988 * @param data
8989 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8990 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
8991 * @return
8992 */
8993HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
8994 const settings::StorageController &data,
8995 const Guid *puuidRegistry,
8996 const Guid *puuidSnapshot)
8997{
8998 HRESULT rc = S_OK;
8999
9000 /* paranoia: detect duplicate attachments */
9001 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9002 it != data.llAttachedDevices.end();
9003 ++it)
9004 {
9005 const settings::AttachedDevice &ad = *it;
9006
9007 for (settings::AttachedDevicesList::const_iterator it2 = it;
9008 it2 != data.llAttachedDevices.end();
9009 ++it2)
9010 {
9011 if (it == it2)
9012 continue;
9013
9014 const settings::AttachedDevice &ad2 = *it2;
9015
9016 if ( ad.lPort == ad2.lPort
9017 && ad.lDevice == ad2.lDevice)
9018 {
9019 return setError(E_FAIL,
9020 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9021 aStorageController->getName().c_str(),
9022 ad.lPort,
9023 ad.lDevice,
9024 mUserData->s.strName.c_str());
9025 }
9026 }
9027 }
9028
9029 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9030 it != data.llAttachedDevices.end();
9031 ++it)
9032 {
9033 const settings::AttachedDevice &dev = *it;
9034 ComObjPtr<Medium> medium;
9035
9036 switch (dev.deviceType)
9037 {
9038 case DeviceType_Floppy:
9039 case DeviceType_DVD:
9040 if (dev.strHostDriveSrc.isNotEmpty())
9041 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9042 else
9043 rc = mParent->findRemoveableMedium(dev.deviceType,
9044 dev.uuid,
9045 false /* fRefresh */,
9046 false /* aSetError */,
9047 medium);
9048 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9049 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9050 rc = S_OK;
9051 break;
9052
9053 case DeviceType_HardDisk:
9054 {
9055 /* find a hard disk by UUID */
9056 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9057 if (FAILED(rc))
9058 {
9059 if (isSnapshotMachine())
9060 {
9061 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9062 // so the user knows that the bad disk is in a snapshot somewhere
9063 com::ErrorInfo info;
9064 return setError(E_FAIL,
9065 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9066 puuidSnapshot->raw(),
9067 info.getText().raw());
9068 }
9069 else
9070 return rc;
9071 }
9072
9073 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9074
9075 if (medium->getType() == MediumType_Immutable)
9076 {
9077 if (isSnapshotMachine())
9078 return setError(E_FAIL,
9079 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9080 "of the virtual machine '%s' ('%s')"),
9081 medium->getLocationFull().c_str(),
9082 dev.uuid.raw(),
9083 puuidSnapshot->raw(),
9084 mUserData->s.strName.c_str(),
9085 mData->m_strConfigFileFull.c_str());
9086
9087 return setError(E_FAIL,
9088 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9089 medium->getLocationFull().c_str(),
9090 dev.uuid.raw(),
9091 mUserData->s.strName.c_str(),
9092 mData->m_strConfigFileFull.c_str());
9093 }
9094
9095 if (medium->getType() == MediumType_MultiAttach)
9096 {
9097 if (isSnapshotMachine())
9098 return setError(E_FAIL,
9099 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9100 "of the virtual machine '%s' ('%s')"),
9101 medium->getLocationFull().c_str(),
9102 dev.uuid.raw(),
9103 puuidSnapshot->raw(),
9104 mUserData->s.strName.c_str(),
9105 mData->m_strConfigFileFull.c_str());
9106
9107 return setError(E_FAIL,
9108 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9109 medium->getLocationFull().c_str(),
9110 dev.uuid.raw(),
9111 mUserData->s.strName.c_str(),
9112 mData->m_strConfigFileFull.c_str());
9113 }
9114
9115 if ( !isSnapshotMachine()
9116 && medium->getChildren().size() != 0
9117 )
9118 return setError(E_FAIL,
9119 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9120 "because it has %d differencing child hard disks"),
9121 medium->getLocationFull().c_str(),
9122 dev.uuid.raw(),
9123 mUserData->s.strName.c_str(),
9124 mData->m_strConfigFileFull.c_str(),
9125 medium->getChildren().size());
9126
9127 if (findAttachment(mMediaData->mAttachments,
9128 medium))
9129 return setError(E_FAIL,
9130 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9131 medium->getLocationFull().c_str(),
9132 dev.uuid.raw(),
9133 mUserData->s.strName.c_str(),
9134 mData->m_strConfigFileFull.c_str());
9135
9136 break;
9137 }
9138
9139 default:
9140 return setError(E_FAIL,
9141 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9142 medium->getLocationFull().c_str(),
9143 mUserData->s.strName.c_str(),
9144 mData->m_strConfigFileFull.c_str());
9145 }
9146
9147 if (FAILED(rc))
9148 break;
9149
9150 /* Bandwidth groups are loaded at this point. */
9151 ComObjPtr<BandwidthGroup> pBwGroup;
9152
9153 if (!dev.strBwGroup.isEmpty())
9154 {
9155 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9156 if (FAILED(rc))
9157 return setError(E_FAIL,
9158 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9159 medium->getLocationFull().c_str(),
9160 dev.strBwGroup.c_str(),
9161 mUserData->s.strName.c_str(),
9162 mData->m_strConfigFileFull.c_str());
9163 pBwGroup->reference();
9164 }
9165
9166 const Bstr controllerName = aStorageController->getName();
9167 ComObjPtr<MediumAttachment> pAttachment;
9168 pAttachment.createObject();
9169 rc = pAttachment->init(this,
9170 medium,
9171 controllerName,
9172 dev.lPort,
9173 dev.lDevice,
9174 dev.deviceType,
9175 false,
9176 dev.fPassThrough,
9177 dev.fTempEject,
9178 dev.fNonRotational,
9179 dev.fDiscard,
9180 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9181 if (FAILED(rc)) break;
9182
9183 /* associate the medium with this machine and snapshot */
9184 if (!medium.isNull())
9185 {
9186 AutoCaller medCaller(medium);
9187 if (FAILED(medCaller.rc())) return medCaller.rc();
9188 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9189
9190 if (isSnapshotMachine())
9191 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9192 else
9193 rc = medium->addBackReference(mData->mUuid);
9194 /* If the medium->addBackReference fails it sets an appropriate
9195 * error message, so no need to do any guesswork here. */
9196
9197 if (puuidRegistry)
9198 // caller wants registry ID to be set on all attached media (OVF import case)
9199 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9200 }
9201
9202 if (FAILED(rc))
9203 break;
9204
9205 /* back up mMediaData to let registeredInit() properly rollback on failure
9206 * (= limited accessibility) */
9207 setModified(IsModified_Storage);
9208 mMediaData.backup();
9209 mMediaData->mAttachments.push_back(pAttachment);
9210 }
9211
9212 return rc;
9213}
9214
9215/**
9216 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9217 *
9218 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9219 * @param aSnapshot where to return the found snapshot
9220 * @param aSetError true to set extended error info on failure
9221 */
9222HRESULT Machine::findSnapshotById(const Guid &aId,
9223 ComObjPtr<Snapshot> &aSnapshot,
9224 bool aSetError /* = false */)
9225{
9226 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9227
9228 if (!mData->mFirstSnapshot)
9229 {
9230 if (aSetError)
9231 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9232 return E_FAIL;
9233 }
9234
9235 if (aId.isZero())
9236 aSnapshot = mData->mFirstSnapshot;
9237 else
9238 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9239
9240 if (!aSnapshot)
9241 {
9242 if (aSetError)
9243 return setError(E_FAIL,
9244 tr("Could not find a snapshot with UUID {%s}"),
9245 aId.toString().c_str());
9246 return E_FAIL;
9247 }
9248
9249 return S_OK;
9250}
9251
9252/**
9253 * Returns the snapshot with the given name or fails of no such snapshot.
9254 *
9255 * @param aName snapshot name to find
9256 * @param aSnapshot where to return the found snapshot
9257 * @param aSetError true to set extended error info on failure
9258 */
9259HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9260 ComObjPtr<Snapshot> &aSnapshot,
9261 bool aSetError /* = false */)
9262{
9263 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9264
9265 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9266
9267 if (!mData->mFirstSnapshot)
9268 {
9269 if (aSetError)
9270 return setError(VBOX_E_OBJECT_NOT_FOUND,
9271 tr("This machine does not have any snapshots"));
9272 return VBOX_E_OBJECT_NOT_FOUND;
9273 }
9274
9275 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9276
9277 if (!aSnapshot)
9278 {
9279 if (aSetError)
9280 return setError(VBOX_E_OBJECT_NOT_FOUND,
9281 tr("Could not find a snapshot named '%s'"), strName.c_str());
9282 return VBOX_E_OBJECT_NOT_FOUND;
9283 }
9284
9285 return S_OK;
9286}
9287
9288/**
9289 * Returns a storage controller object with the given name.
9290 *
9291 * @param aName storage controller name to find
9292 * @param aStorageController where to return the found storage controller
9293 * @param aSetError true to set extended error info on failure
9294 */
9295HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9296 ComObjPtr<StorageController> &aStorageController,
9297 bool aSetError /* = false */)
9298{
9299 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9300
9301 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9302 it != mStorageControllers->end();
9303 ++it)
9304 {
9305 if ((*it)->getName() == aName)
9306 {
9307 aStorageController = (*it);
9308 return S_OK;
9309 }
9310 }
9311
9312 if (aSetError)
9313 return setError(VBOX_E_OBJECT_NOT_FOUND,
9314 tr("Could not find a storage controller named '%s'"),
9315 aName.c_str());
9316 return VBOX_E_OBJECT_NOT_FOUND;
9317}
9318
9319HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9320 MediaData::AttachmentList &atts)
9321{
9322 AutoCaller autoCaller(this);
9323 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9324
9325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9326
9327 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9328 it != mMediaData->mAttachments.end();
9329 ++it)
9330 {
9331 const ComObjPtr<MediumAttachment> &pAtt = *it;
9332
9333 // should never happen, but deal with NULL pointers in the list.
9334 AssertStmt(!pAtt.isNull(), continue);
9335
9336 // getControllerName() needs caller+read lock
9337 AutoCaller autoAttCaller(pAtt);
9338 if (FAILED(autoAttCaller.rc()))
9339 {
9340 atts.clear();
9341 return autoAttCaller.rc();
9342 }
9343 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9344
9345 if (pAtt->getControllerName() == aName)
9346 atts.push_back(pAtt);
9347 }
9348
9349 return S_OK;
9350}
9351
9352/**
9353 * Helper for #saveSettings. Cares about renaming the settings directory and
9354 * file if the machine name was changed and about creating a new settings file
9355 * if this is a new machine.
9356 *
9357 * @note Must be never called directly but only from #saveSettings().
9358 */
9359HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9360{
9361 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9362
9363 HRESULT rc = S_OK;
9364
9365 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9366
9367 /// @todo need to handle primary group change, too
9368
9369 /* attempt to rename the settings file if machine name is changed */
9370 if ( mUserData->s.fNameSync
9371 && mUserData.isBackedUp()
9372 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9373 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9374 )
9375 {
9376 bool dirRenamed = false;
9377 bool fileRenamed = false;
9378
9379 Utf8Str configFile, newConfigFile;
9380 Utf8Str configFilePrev, newConfigFilePrev;
9381 Utf8Str configDir, newConfigDir;
9382
9383 do
9384 {
9385 int vrc = VINF_SUCCESS;
9386
9387 Utf8Str name = mUserData.backedUpData()->s.strName;
9388 Utf8Str newName = mUserData->s.strName;
9389 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9390 if (group == "/")
9391 group.setNull();
9392 Utf8Str newGroup = mUserData->s.llGroups.front();
9393 if (newGroup == "/")
9394 newGroup.setNull();
9395
9396 configFile = mData->m_strConfigFileFull;
9397
9398 /* first, rename the directory if it matches the group and machine name */
9399 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9400 group.c_str(), RTPATH_DELIMITER, name.c_str());
9401 /** @todo hack, make somehow use of ComposeMachineFilename */
9402 if (mUserData->s.fDirectoryIncludesUUID)
9403 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9404 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9405 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9406 /** @todo hack, make somehow use of ComposeMachineFilename */
9407 if (mUserData->s.fDirectoryIncludesUUID)
9408 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9409 configDir = configFile;
9410 configDir.stripFilename();
9411 newConfigDir = configDir;
9412 if ( configDir.length() >= groupPlusName.length()
9413 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9414 {
9415 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9416 Utf8Str newConfigBaseDir(newConfigDir);
9417 newConfigDir.append(newGroupPlusName);
9418 /* consistency: use \ if appropriate on the platform */
9419 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9420 /* new dir and old dir cannot be equal here because of 'if'
9421 * above and because name != newName */
9422 Assert(configDir != newConfigDir);
9423 if (!fSettingsFileIsNew)
9424 {
9425 /* perform real rename only if the machine is not new */
9426 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9427 if ( vrc == VERR_FILE_NOT_FOUND
9428 || vrc == VERR_PATH_NOT_FOUND)
9429 {
9430 /* create the parent directory, then retry renaming */
9431 Utf8Str parent(newConfigDir);
9432 parent.stripFilename();
9433 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9434 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9435 }
9436 if (RT_FAILURE(vrc))
9437 {
9438 rc = setError(E_FAIL,
9439 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9440 configDir.c_str(),
9441 newConfigDir.c_str(),
9442 vrc);
9443 break;
9444 }
9445 /* delete subdirectories which are no longer needed */
9446 Utf8Str dir(configDir);
9447 dir.stripFilename();
9448 while (dir != newConfigBaseDir && dir != ".")
9449 {
9450 vrc = RTDirRemove(dir.c_str());
9451 if (RT_FAILURE(vrc))
9452 break;
9453 dir.stripFilename();
9454 }
9455 dirRenamed = true;
9456 }
9457 }
9458
9459 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9460 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9461
9462 /* then try to rename the settings file itself */
9463 if (newConfigFile != configFile)
9464 {
9465 /* get the path to old settings file in renamed directory */
9466 configFile = Utf8StrFmt("%s%c%s",
9467 newConfigDir.c_str(),
9468 RTPATH_DELIMITER,
9469 RTPathFilename(configFile.c_str()));
9470 if (!fSettingsFileIsNew)
9471 {
9472 /* perform real rename only if the machine is not new */
9473 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9474 if (RT_FAILURE(vrc))
9475 {
9476 rc = setError(E_FAIL,
9477 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9478 configFile.c_str(),
9479 newConfigFile.c_str(),
9480 vrc);
9481 break;
9482 }
9483 fileRenamed = true;
9484 configFilePrev = configFile;
9485 configFilePrev += "-prev";
9486 newConfigFilePrev = newConfigFile;
9487 newConfigFilePrev += "-prev";
9488 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9489 }
9490 }
9491
9492 // update m_strConfigFileFull amd mConfigFile
9493 mData->m_strConfigFileFull = newConfigFile;
9494 // compute the relative path too
9495 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9496
9497 // store the old and new so that VirtualBox::saveSettings() can update
9498 // the media registry
9499 if ( mData->mRegistered
9500 && configDir != newConfigDir)
9501 {
9502 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9503
9504 if (pfNeedsGlobalSaveSettings)
9505 *pfNeedsGlobalSaveSettings = true;
9506 }
9507
9508 // in the saved state file path, replace the old directory with the new directory
9509 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9510 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9511
9512 // and do the same thing for the saved state file paths of all the online snapshots
9513 if (mData->mFirstSnapshot)
9514 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9515 newConfigDir.c_str());
9516 }
9517 while (0);
9518
9519 if (FAILED(rc))
9520 {
9521 /* silently try to rename everything back */
9522 if (fileRenamed)
9523 {
9524 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9525 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9526 }
9527 if (dirRenamed)
9528 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9529 }
9530
9531 if (FAILED(rc)) return rc;
9532 }
9533
9534 if (fSettingsFileIsNew)
9535 {
9536 /* create a virgin config file */
9537 int vrc = VINF_SUCCESS;
9538
9539 /* ensure the settings directory exists */
9540 Utf8Str path(mData->m_strConfigFileFull);
9541 path.stripFilename();
9542 if (!RTDirExists(path.c_str()))
9543 {
9544 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9545 if (RT_FAILURE(vrc))
9546 {
9547 return setError(E_FAIL,
9548 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9549 path.c_str(),
9550 vrc);
9551 }
9552 }
9553
9554 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9555 path = Utf8Str(mData->m_strConfigFileFull);
9556 RTFILE f = NIL_RTFILE;
9557 vrc = RTFileOpen(&f, path.c_str(),
9558 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9559 if (RT_FAILURE(vrc))
9560 return setError(E_FAIL,
9561 tr("Could not create the settings file '%s' (%Rrc)"),
9562 path.c_str(),
9563 vrc);
9564 RTFileClose(f);
9565 }
9566
9567 return rc;
9568}
9569
9570/**
9571 * Saves and commits machine data, user data and hardware data.
9572 *
9573 * Note that on failure, the data remains uncommitted.
9574 *
9575 * @a aFlags may combine the following flags:
9576 *
9577 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9578 * Used when saving settings after an operation that makes them 100%
9579 * correspond to the settings from the current snapshot.
9580 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9581 * #isReallyModified() returns false. This is necessary for cases when we
9582 * change machine data directly, not through the backup()/commit() mechanism.
9583 * - SaveS_Force: settings will be saved without doing a deep compare of the
9584 * settings structures. This is used when this is called because snapshots
9585 * have changed to avoid the overhead of the deep compare.
9586 *
9587 * @note Must be called from under this object's write lock. Locks children for
9588 * writing.
9589 *
9590 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9591 * initialized to false and that will be set to true by this function if
9592 * the caller must invoke VirtualBox::saveSettings() because the global
9593 * settings have changed. This will happen if a machine rename has been
9594 * saved and the global machine and media registries will therefore need
9595 * updating.
9596 */
9597HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9598 int aFlags /*= 0*/)
9599{
9600 LogFlowThisFuncEnter();
9601
9602 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9603
9604 /* make sure child objects are unable to modify the settings while we are
9605 * saving them */
9606 ensureNoStateDependencies();
9607
9608 AssertReturn(!isSnapshotMachine(),
9609 E_FAIL);
9610
9611 HRESULT rc = S_OK;
9612 bool fNeedsWrite = false;
9613
9614 /* First, prepare to save settings. It will care about renaming the
9615 * settings directory and file if the machine name was changed and about
9616 * creating a new settings file if this is a new machine. */
9617 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9618 if (FAILED(rc)) return rc;
9619
9620 // keep a pointer to the current settings structures
9621 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9622 settings::MachineConfigFile *pNewConfig = NULL;
9623
9624 try
9625 {
9626 // make a fresh one to have everyone write stuff into
9627 pNewConfig = new settings::MachineConfigFile(NULL);
9628 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9629
9630 // now go and copy all the settings data from COM to the settings structures
9631 // (this calles saveSettings() on all the COM objects in the machine)
9632 copyMachineDataToSettings(*pNewConfig);
9633
9634 if (aFlags & SaveS_ResetCurStateModified)
9635 {
9636 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9637 mData->mCurrentStateModified = FALSE;
9638 fNeedsWrite = true; // always, no need to compare
9639 }
9640 else if (aFlags & SaveS_Force)
9641 {
9642 fNeedsWrite = true; // always, no need to compare
9643 }
9644 else
9645 {
9646 if (!mData->mCurrentStateModified)
9647 {
9648 // do a deep compare of the settings that we just saved with the settings
9649 // previously stored in the config file; this invokes MachineConfigFile::operator==
9650 // which does a deep compare of all the settings, which is expensive but less expensive
9651 // than writing out XML in vain
9652 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9653
9654 // could still be modified if any settings changed
9655 mData->mCurrentStateModified = fAnySettingsChanged;
9656
9657 fNeedsWrite = fAnySettingsChanged;
9658 }
9659 else
9660 fNeedsWrite = true;
9661 }
9662
9663 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9664
9665 if (fNeedsWrite)
9666 // now spit it all out!
9667 pNewConfig->write(mData->m_strConfigFileFull);
9668
9669 mData->pMachineConfigFile = pNewConfig;
9670 delete pOldConfig;
9671 commit();
9672
9673 // after saving settings, we are no longer different from the XML on disk
9674 mData->flModifications = 0;
9675 }
9676 catch (HRESULT err)
9677 {
9678 // we assume that error info is set by the thrower
9679 rc = err;
9680
9681 // restore old config
9682 delete pNewConfig;
9683 mData->pMachineConfigFile = pOldConfig;
9684 }
9685 catch (...)
9686 {
9687 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9688 }
9689
9690 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9691 {
9692 /* Fire the data change event, even on failure (since we've already
9693 * committed all data). This is done only for SessionMachines because
9694 * mutable Machine instances are always not registered (i.e. private
9695 * to the client process that creates them) and thus don't need to
9696 * inform callbacks. */
9697 if (isSessionMachine())
9698 mParent->onMachineDataChange(mData->mUuid);
9699 }
9700
9701 LogFlowThisFunc(("rc=%08X\n", rc));
9702 LogFlowThisFuncLeave();
9703 return rc;
9704}
9705
9706/**
9707 * Implementation for saving the machine settings into the given
9708 * settings::MachineConfigFile instance. This copies machine extradata
9709 * from the previous machine config file in the instance data, if any.
9710 *
9711 * This gets called from two locations:
9712 *
9713 * -- Machine::saveSettings(), during the regular XML writing;
9714 *
9715 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9716 * exported to OVF and we write the VirtualBox proprietary XML
9717 * into a <vbox:Machine> tag.
9718 *
9719 * This routine fills all the fields in there, including snapshots, *except*
9720 * for the following:
9721 *
9722 * -- fCurrentStateModified. There is some special logic associated with that.
9723 *
9724 * The caller can then call MachineConfigFile::write() or do something else
9725 * with it.
9726 *
9727 * Caller must hold the machine lock!
9728 *
9729 * This throws XML errors and HRESULT, so the caller must have a catch block!
9730 */
9731void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9732{
9733 // deep copy extradata
9734 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9735
9736 config.uuid = mData->mUuid;
9737
9738 // copy name, description, OS type, teleport, UTC etc.
9739 config.machineUserData = mUserData->s;
9740
9741 if ( mData->mMachineState == MachineState_Saved
9742 || mData->mMachineState == MachineState_Restoring
9743 // when deleting a snapshot we may or may not have a saved state in the current state,
9744 // so let's not assert here please
9745 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9746 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9747 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9748 && (!mSSData->strStateFilePath.isEmpty())
9749 )
9750 )
9751 {
9752 Assert(!mSSData->strStateFilePath.isEmpty());
9753 /* try to make the file name relative to the settings file dir */
9754 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9755 }
9756 else
9757 {
9758 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9759 config.strStateFile.setNull();
9760 }
9761
9762 if (mData->mCurrentSnapshot)
9763 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9764 else
9765 config.uuidCurrentSnapshot.clear();
9766
9767 config.timeLastStateChange = mData->mLastStateChange;
9768 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9769 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9770
9771 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9772 if (FAILED(rc)) throw rc;
9773
9774 rc = saveStorageControllers(config.storageMachine);
9775 if (FAILED(rc)) throw rc;
9776
9777 // save machine's media registry if this is VirtualBox 4.0 or later
9778 if (config.canHaveOwnMediaRegistry())
9779 {
9780 // determine machine folder
9781 Utf8Str strMachineFolder = getSettingsFileFull();
9782 strMachineFolder.stripFilename();
9783 mParent->saveMediaRegistry(config.mediaRegistry,
9784 getId(), // only media with registry ID == machine UUID
9785 strMachineFolder);
9786 // this throws HRESULT
9787 }
9788
9789 // save snapshots
9790 rc = saveAllSnapshots(config);
9791 if (FAILED(rc)) throw rc;
9792}
9793
9794/**
9795 * Saves all snapshots of the machine into the given machine config file. Called
9796 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9797 * @param config
9798 * @return
9799 */
9800HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9801{
9802 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9803
9804 HRESULT rc = S_OK;
9805
9806 try
9807 {
9808 config.llFirstSnapshot.clear();
9809
9810 if (mData->mFirstSnapshot)
9811 {
9812 settings::Snapshot snapNew;
9813 config.llFirstSnapshot.push_back(snapNew);
9814
9815 // get reference to the fresh copy of the snapshot on the list and
9816 // work on that copy directly to avoid excessive copying later
9817 settings::Snapshot &snap = config.llFirstSnapshot.front();
9818
9819 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9820 if (FAILED(rc)) throw rc;
9821 }
9822
9823// if (mType == IsSessionMachine)
9824// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9825
9826 }
9827 catch (HRESULT err)
9828 {
9829 /* we assume that error info is set by the thrower */
9830 rc = err;
9831 }
9832 catch (...)
9833 {
9834 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9835 }
9836
9837 return rc;
9838}
9839
9840/**
9841 * Saves the VM hardware configuration. It is assumed that the
9842 * given node is empty.
9843 *
9844 * @param data Reference to the settings object for the hardware config.
9845 * @param pDbg Pointer to the settings object for the debugging config
9846 * which happens to live in mHWData.
9847 * @param pAutostart Pointer to the settings object for the autostart config
9848 * which happens to live in mHWData.
9849 */
9850HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9851 settings::Autostart *pAutostart)
9852{
9853 HRESULT rc = S_OK;
9854
9855 try
9856 {
9857 /* The hardware version attribute (optional).
9858 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9859 if ( mHWData->mHWVersion == "1"
9860 && mSSData->strStateFilePath.isEmpty()
9861 )
9862 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. */
9863
9864 data.strVersion = mHWData->mHWVersion;
9865 data.uuid = mHWData->mHardwareUUID;
9866
9867 // CPU
9868 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9869 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9870 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9871 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9872 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9873 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9874 data.fPAE = !!mHWData->mPAEEnabled;
9875 data.enmLongMode = mHWData->mLongMode;
9876 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9877
9878 /* Standard and Extended CPUID leafs. */
9879 data.llCpuIdLeafs.clear();
9880 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9881 {
9882 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9883 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9884 }
9885 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9886 {
9887 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9888 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9889 }
9890
9891 data.cCPUs = mHWData->mCPUCount;
9892 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9893 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9894
9895 data.llCpus.clear();
9896 if (data.fCpuHotPlug)
9897 {
9898 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9899 {
9900 if (mHWData->mCPUAttached[idx])
9901 {
9902 settings::Cpu cpu;
9903 cpu.ulId = idx;
9904 data.llCpus.push_back(cpu);
9905 }
9906 }
9907 }
9908
9909 // memory
9910 data.ulMemorySizeMB = mHWData->mMemorySize;
9911 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9912
9913 // firmware
9914 data.firmwareType = mHWData->mFirmwareType;
9915
9916 // HID
9917 data.pointingHIDType = mHWData->mPointingHIDType;
9918 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9919
9920 // chipset
9921 data.chipsetType = mHWData->mChipsetType;
9922
9923 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9924 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9925
9926 // HPET
9927 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9928
9929 // boot order
9930 data.mapBootOrder.clear();
9931 for (size_t i = 0;
9932 i < RT_ELEMENTS(mHWData->mBootOrder);
9933 ++i)
9934 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9935
9936 // display
9937 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9938 data.cMonitors = mHWData->mMonitorCount;
9939 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9940 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9941 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9942 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9943 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9944 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9945
9946 /* VRDEServer settings (optional) */
9947 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9948 if (FAILED(rc)) throw rc;
9949
9950 /* BIOS (required) */
9951 rc = mBIOSSettings->saveSettings(data.biosSettings);
9952 if (FAILED(rc)) throw rc;
9953
9954 /* USB Controller (required) */
9955 rc = mUSBController->saveSettings(data.usbController);
9956 if (FAILED(rc)) throw rc;
9957
9958 /* Network adapters (required) */
9959 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
9960 data.llNetworkAdapters.clear();
9961 /* Write out only the nominal number of network adapters for this
9962 * chipset type. Since Machine::commit() hasn't been called there
9963 * may be extra NIC settings in the vector. */
9964 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
9965 {
9966 settings::NetworkAdapter nic;
9967 nic.ulSlot = slot;
9968 /* paranoia check... must not be NULL, but must not crash either. */
9969 if (mNetworkAdapters[slot])
9970 {
9971 rc = mNetworkAdapters[slot]->saveSettings(nic);
9972 if (FAILED(rc)) throw rc;
9973
9974 data.llNetworkAdapters.push_back(nic);
9975 }
9976 }
9977
9978 /* Serial ports */
9979 data.llSerialPorts.clear();
9980 for (ULONG slot = 0;
9981 slot < RT_ELEMENTS(mSerialPorts);
9982 ++slot)
9983 {
9984 settings::SerialPort s;
9985 s.ulSlot = slot;
9986 rc = mSerialPorts[slot]->saveSettings(s);
9987 if (FAILED(rc)) return rc;
9988
9989 data.llSerialPorts.push_back(s);
9990 }
9991
9992 /* Parallel ports */
9993 data.llParallelPorts.clear();
9994 for (ULONG slot = 0;
9995 slot < RT_ELEMENTS(mParallelPorts);
9996 ++slot)
9997 {
9998 settings::ParallelPort p;
9999 p.ulSlot = slot;
10000 rc = mParallelPorts[slot]->saveSettings(p);
10001 if (FAILED(rc)) return rc;
10002
10003 data.llParallelPorts.push_back(p);
10004 }
10005
10006 /* Audio adapter */
10007 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10008 if (FAILED(rc)) return rc;
10009
10010 /* Shared folders */
10011 data.llSharedFolders.clear();
10012 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10013 it != mHWData->mSharedFolders.end();
10014 ++it)
10015 {
10016 SharedFolder *pSF = *it;
10017 AutoCaller sfCaller(pSF);
10018 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10019 settings::SharedFolder sf;
10020 sf.strName = pSF->getName();
10021 sf.strHostPath = pSF->getHostPath();
10022 sf.fWritable = !!pSF->isWritable();
10023 sf.fAutoMount = !!pSF->isAutoMounted();
10024
10025 data.llSharedFolders.push_back(sf);
10026 }
10027
10028 // clipboard
10029 data.clipboardMode = mHWData->mClipboardMode;
10030
10031 // drag'n'drop
10032 data.dragAndDropMode = mHWData->mDragAndDropMode;
10033
10034 /* Guest */
10035 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10036
10037 // IO settings
10038 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10039 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10040
10041 /* BandwidthControl (required) */
10042 rc = mBandwidthControl->saveSettings(data.ioSettings);
10043 if (FAILED(rc)) throw rc;
10044
10045 /* Host PCI devices */
10046 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10047 it != mHWData->mPCIDeviceAssignments.end();
10048 ++it)
10049 {
10050 ComObjPtr<PCIDeviceAttachment> pda = *it;
10051 settings::HostPCIDeviceAttachment hpda;
10052
10053 rc = pda->saveSettings(hpda);
10054 if (FAILED(rc)) throw rc;
10055
10056 data.pciAttachments.push_back(hpda);
10057 }
10058
10059
10060 // guest properties
10061 data.llGuestProperties.clear();
10062#ifdef VBOX_WITH_GUEST_PROPS
10063 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10064 it != mHWData->mGuestProperties.end();
10065 ++it)
10066 {
10067 HWData::GuestProperty property = it->second;
10068
10069 /* Remove transient guest properties at shutdown unless we
10070 * are saving state */
10071 if ( ( mData->mMachineState == MachineState_PoweredOff
10072 || mData->mMachineState == MachineState_Aborted
10073 || mData->mMachineState == MachineState_Teleported)
10074 && ( property.mFlags & guestProp::TRANSIENT
10075 || property.mFlags & guestProp::TRANSRESET))
10076 continue;
10077 settings::GuestProperty prop;
10078 prop.strName = it->first;
10079 prop.strValue = property.strValue;
10080 prop.timestamp = property.mTimestamp;
10081 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10082 guestProp::writeFlags(property.mFlags, szFlags);
10083 prop.strFlags = szFlags;
10084
10085 data.llGuestProperties.push_back(prop);
10086 }
10087
10088 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10089 /* I presume this doesn't require a backup(). */
10090 mData->mGuestPropertiesModified = FALSE;
10091#endif /* VBOX_WITH_GUEST_PROPS defined */
10092
10093 *pDbg = mHWData->mDebugging;
10094 *pAutostart = mHWData->mAutostart;
10095
10096 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10097 }
10098 catch(std::bad_alloc &)
10099 {
10100 return E_OUTOFMEMORY;
10101 }
10102
10103 AssertComRC(rc);
10104 return rc;
10105}
10106
10107/**
10108 * Saves the storage controller configuration.
10109 *
10110 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10111 */
10112HRESULT Machine::saveStorageControllers(settings::Storage &data)
10113{
10114 data.llStorageControllers.clear();
10115
10116 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10117 it != mStorageControllers->end();
10118 ++it)
10119 {
10120 HRESULT rc;
10121 ComObjPtr<StorageController> pCtl = *it;
10122
10123 settings::StorageController ctl;
10124 ctl.strName = pCtl->getName();
10125 ctl.controllerType = pCtl->getControllerType();
10126 ctl.storageBus = pCtl->getStorageBus();
10127 ctl.ulInstance = pCtl->getInstance();
10128 ctl.fBootable = pCtl->getBootable();
10129
10130 /* Save the port count. */
10131 ULONG portCount;
10132 rc = pCtl->COMGETTER(PortCount)(&portCount);
10133 ComAssertComRCRet(rc, rc);
10134 ctl.ulPortCount = portCount;
10135
10136 /* Save fUseHostIOCache */
10137 BOOL fUseHostIOCache;
10138 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10139 ComAssertComRCRet(rc, rc);
10140 ctl.fUseHostIOCache = !!fUseHostIOCache;
10141
10142 /* Save IDE emulation settings. */
10143 if (ctl.controllerType == StorageControllerType_IntelAhci)
10144 {
10145 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10146 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10147 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10148 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10149 )
10150 ComAssertComRCRet(rc, rc);
10151 }
10152
10153 /* save the devices now. */
10154 rc = saveStorageDevices(pCtl, ctl);
10155 ComAssertComRCRet(rc, rc);
10156
10157 data.llStorageControllers.push_back(ctl);
10158 }
10159
10160 return S_OK;
10161}
10162
10163/**
10164 * Saves the hard disk configuration.
10165 */
10166HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10167 settings::StorageController &data)
10168{
10169 MediaData::AttachmentList atts;
10170
10171 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10172 if (FAILED(rc)) return rc;
10173
10174 data.llAttachedDevices.clear();
10175 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10176 it != atts.end();
10177 ++it)
10178 {
10179 settings::AttachedDevice dev;
10180
10181 MediumAttachment *pAttach = *it;
10182 Medium *pMedium = pAttach->getMedium();
10183
10184 dev.deviceType = pAttach->getType();
10185 dev.lPort = pAttach->getPort();
10186 dev.lDevice = pAttach->getDevice();
10187 if (pMedium)
10188 {
10189 if (pMedium->isHostDrive())
10190 dev.strHostDriveSrc = pMedium->getLocationFull();
10191 else
10192 dev.uuid = pMedium->getId();
10193 dev.fPassThrough = pAttach->getPassthrough();
10194 dev.fTempEject = pAttach->getTempEject();
10195 dev.fNonRotational = pAttach->getNonRotational();
10196 dev.fDiscard = pAttach->getDiscard();
10197 }
10198
10199 dev.strBwGroup = pAttach->getBandwidthGroup();
10200
10201 data.llAttachedDevices.push_back(dev);
10202 }
10203
10204 return S_OK;
10205}
10206
10207/**
10208 * Saves machine state settings as defined by aFlags
10209 * (SaveSTS_* values).
10210 *
10211 * @param aFlags Combination of SaveSTS_* flags.
10212 *
10213 * @note Locks objects for writing.
10214 */
10215HRESULT Machine::saveStateSettings(int aFlags)
10216{
10217 if (aFlags == 0)
10218 return S_OK;
10219
10220 AutoCaller autoCaller(this);
10221 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10222
10223 /* This object's write lock is also necessary to serialize file access
10224 * (prevent concurrent reads and writes) */
10225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10226
10227 HRESULT rc = S_OK;
10228
10229 Assert(mData->pMachineConfigFile);
10230
10231 try
10232 {
10233 if (aFlags & SaveSTS_CurStateModified)
10234 mData->pMachineConfigFile->fCurrentStateModified = true;
10235
10236 if (aFlags & SaveSTS_StateFilePath)
10237 {
10238 if (!mSSData->strStateFilePath.isEmpty())
10239 /* try to make the file name relative to the settings file dir */
10240 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10241 else
10242 mData->pMachineConfigFile->strStateFile.setNull();
10243 }
10244
10245 if (aFlags & SaveSTS_StateTimeStamp)
10246 {
10247 Assert( mData->mMachineState != MachineState_Aborted
10248 || mSSData->strStateFilePath.isEmpty());
10249
10250 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10251
10252 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10253//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10254 }
10255
10256 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10257 }
10258 catch (...)
10259 {
10260 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10261 }
10262
10263 return rc;
10264}
10265
10266/**
10267 * Ensures that the given medium is added to a media registry. If this machine
10268 * was created with 4.0 or later, then the machine registry is used. Otherwise
10269 * the global VirtualBox media registry is used.
10270 *
10271 * Caller must NOT hold machine lock, media tree or any medium locks!
10272 *
10273 * @param pMedium
10274 */
10275void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10276{
10277 /* Paranoia checks: do not hold machine or media tree locks. */
10278 AssertReturnVoid(!isWriteLockOnCurrentThread());
10279 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10280
10281 ComObjPtr<Medium> pBase;
10282 {
10283 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10284 pBase = pMedium->getBase();
10285 }
10286
10287 /* Paranoia checks: do not hold medium locks. */
10288 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10289 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10290
10291 // decide which medium registry to use now that the medium is attached:
10292 Guid uuid;
10293 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10294 // machine XML is VirtualBox 4.0 or higher:
10295 uuid = getId(); // machine UUID
10296 else
10297 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10298
10299 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10300 mParent->markRegistryModified(uuid);
10301
10302 /* For more complex hard disk structures it can happen that the base
10303 * medium isn't yet associated with any medium registry. Do that now. */
10304 if (pMedium != pBase)
10305 {
10306 if (pBase->addRegistry(uuid, true /* fRecurse */))
10307 mParent->markRegistryModified(uuid);
10308 }
10309}
10310
10311/**
10312 * Creates differencing hard disks for all normal hard disks attached to this
10313 * machine and a new set of attachments to refer to created disks.
10314 *
10315 * Used when taking a snapshot or when deleting the current state. Gets called
10316 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10317 *
10318 * This method assumes that mMediaData contains the original hard disk attachments
10319 * it needs to create diffs for. On success, these attachments will be replaced
10320 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10321 * called to delete created diffs which will also rollback mMediaData and restore
10322 * whatever was backed up before calling this method.
10323 *
10324 * Attachments with non-normal hard disks are left as is.
10325 *
10326 * If @a aOnline is @c false then the original hard disks that require implicit
10327 * diffs will be locked for reading. Otherwise it is assumed that they are
10328 * already locked for writing (when the VM was started). Note that in the latter
10329 * case it is responsibility of the caller to lock the newly created diffs for
10330 * writing if this method succeeds.
10331 *
10332 * @param aProgress Progress object to run (must contain at least as
10333 * many operations left as the number of hard disks
10334 * attached).
10335 * @param aOnline Whether the VM was online prior to this operation.
10336 *
10337 * @note The progress object is not marked as completed, neither on success nor
10338 * on failure. This is a responsibility of the caller.
10339 *
10340 * @note Locks this object and the media tree for writing.
10341 */
10342HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10343 ULONG aWeight,
10344 bool aOnline)
10345{
10346 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10347
10348 AutoCaller autoCaller(this);
10349 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10350
10351 AutoMultiWriteLock2 alock(this->lockHandle(),
10352 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10353
10354 /* must be in a protective state because we release the lock below */
10355 AssertReturn( mData->mMachineState == MachineState_Saving
10356 || mData->mMachineState == MachineState_LiveSnapshotting
10357 || mData->mMachineState == MachineState_RestoringSnapshot
10358 || mData->mMachineState == MachineState_DeletingSnapshot
10359 , E_FAIL);
10360
10361 HRESULT rc = S_OK;
10362
10363 // use appropriate locked media map (online or offline)
10364 MediumLockListMap lockedMediaOffline;
10365 MediumLockListMap *lockedMediaMap;
10366 if (aOnline)
10367 lockedMediaMap = &mData->mSession.mLockedMedia;
10368 else
10369 lockedMediaMap = &lockedMediaOffline;
10370
10371 try
10372 {
10373 if (!aOnline)
10374 {
10375 /* lock all attached hard disks early to detect "in use"
10376 * situations before creating actual diffs */
10377 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10378 it != mMediaData->mAttachments.end();
10379 ++it)
10380 {
10381 MediumAttachment* pAtt = *it;
10382 if (pAtt->getType() == DeviceType_HardDisk)
10383 {
10384 Medium* pMedium = pAtt->getMedium();
10385 Assert(pMedium);
10386
10387 MediumLockList *pMediumLockList(new MediumLockList());
10388 alock.release();
10389 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10390 false /* fMediumLockWrite */,
10391 NULL,
10392 *pMediumLockList);
10393 alock.acquire();
10394 if (FAILED(rc))
10395 {
10396 delete pMediumLockList;
10397 throw rc;
10398 }
10399 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10400 if (FAILED(rc))
10401 {
10402 throw setError(rc,
10403 tr("Collecting locking information for all attached media failed"));
10404 }
10405 }
10406 }
10407
10408 /* Now lock all media. If this fails, nothing is locked. */
10409 alock.release();
10410 rc = lockedMediaMap->Lock();
10411 alock.acquire();
10412 if (FAILED(rc))
10413 {
10414 throw setError(rc,
10415 tr("Locking of attached media failed"));
10416 }
10417 }
10418
10419 /* remember the current list (note that we don't use backup() since
10420 * mMediaData may be already backed up) */
10421 MediaData::AttachmentList atts = mMediaData->mAttachments;
10422
10423 /* start from scratch */
10424 mMediaData->mAttachments.clear();
10425
10426 /* go through remembered attachments and create diffs for normal hard
10427 * disks and attach them */
10428 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10429 it != atts.end();
10430 ++it)
10431 {
10432 MediumAttachment* pAtt = *it;
10433
10434 DeviceType_T devType = pAtt->getType();
10435 Medium* pMedium = pAtt->getMedium();
10436
10437 if ( devType != DeviceType_HardDisk
10438 || pMedium == NULL
10439 || pMedium->getType() != MediumType_Normal)
10440 {
10441 /* copy the attachment as is */
10442
10443 /** @todo the progress object created in Console::TakeSnaphot
10444 * only expects operations for hard disks. Later other
10445 * device types need to show up in the progress as well. */
10446 if (devType == DeviceType_HardDisk)
10447 {
10448 if (pMedium == NULL)
10449 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10450 aWeight); // weight
10451 else
10452 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10453 pMedium->getBase()->getName().c_str()).raw(),
10454 aWeight); // weight
10455 }
10456
10457 mMediaData->mAttachments.push_back(pAtt);
10458 continue;
10459 }
10460
10461 /* need a diff */
10462 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10463 pMedium->getBase()->getName().c_str()).raw(),
10464 aWeight); // weight
10465
10466 Utf8Str strFullSnapshotFolder;
10467 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10468
10469 ComObjPtr<Medium> diff;
10470 diff.createObject();
10471 // store the diff in the same registry as the parent
10472 // (this cannot fail here because we can't create implicit diffs for
10473 // unregistered images)
10474 Guid uuidRegistryParent;
10475 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10476 Assert(fInRegistry); NOREF(fInRegistry);
10477 rc = diff->init(mParent,
10478 pMedium->getPreferredDiffFormat(),
10479 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10480 uuidRegistryParent);
10481 if (FAILED(rc)) throw rc;
10482
10483 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10484 * the push_back? Looks like we're going to release medium with the
10485 * wrong kind of lock (general issue with if we fail anywhere at all)
10486 * and an orphaned VDI in the snapshots folder. */
10487
10488 /* update the appropriate lock list */
10489 MediumLockList *pMediumLockList;
10490 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10491 AssertComRCThrowRC(rc);
10492 if (aOnline)
10493 {
10494 alock.release();
10495 /* The currently attached medium will be read-only, change
10496 * the lock type to read. */
10497 rc = pMediumLockList->Update(pMedium, false);
10498 alock.acquire();
10499 AssertComRCThrowRC(rc);
10500 }
10501
10502 /* release the locks before the potentially lengthy operation */
10503 alock.release();
10504 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10505 pMediumLockList,
10506 NULL /* aProgress */,
10507 true /* aWait */);
10508 alock.acquire();
10509 if (FAILED(rc)) throw rc;
10510
10511 rc = lockedMediaMap->Unlock();
10512 AssertComRCThrowRC(rc);
10513 alock.release();
10514 rc = pMediumLockList->Append(diff, true);
10515 alock.acquire();
10516 AssertComRCThrowRC(rc);
10517 alock.release();
10518 rc = lockedMediaMap->Lock();
10519 alock.acquire();
10520 AssertComRCThrowRC(rc);
10521
10522 rc = diff->addBackReference(mData->mUuid);
10523 AssertComRCThrowRC(rc);
10524
10525 /* add a new attachment */
10526 ComObjPtr<MediumAttachment> attachment;
10527 attachment.createObject();
10528 rc = attachment->init(this,
10529 diff,
10530 pAtt->getControllerName(),
10531 pAtt->getPort(),
10532 pAtt->getDevice(),
10533 DeviceType_HardDisk,
10534 true /* aImplicit */,
10535 false /* aPassthrough */,
10536 false /* aTempEject */,
10537 pAtt->getNonRotational(),
10538 pAtt->getDiscard(),
10539 pAtt->getBandwidthGroup());
10540 if (FAILED(rc)) throw rc;
10541
10542 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10543 AssertComRCThrowRC(rc);
10544 mMediaData->mAttachments.push_back(attachment);
10545 }
10546 }
10547 catch (HRESULT aRC) { rc = aRC; }
10548
10549 /* unlock all hard disks we locked when there is no VM */
10550 if (!aOnline)
10551 {
10552 ErrorInfoKeeper eik;
10553
10554 HRESULT rc1 = lockedMediaMap->Clear();
10555 AssertComRC(rc1);
10556 }
10557
10558 return rc;
10559}
10560
10561/**
10562 * Deletes implicit differencing hard disks created either by
10563 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10564 *
10565 * Note that to delete hard disks created by #AttachDevice() this method is
10566 * called from #fixupMedia() when the changes are rolled back.
10567 *
10568 * @note Locks this object and the media tree for writing.
10569 */
10570HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10571{
10572 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10573
10574 AutoCaller autoCaller(this);
10575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10576
10577 AutoMultiWriteLock2 alock(this->lockHandle(),
10578 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10579
10580 /* We absolutely must have backed up state. */
10581 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10582
10583 /* Check if there are any implicitly created diff images. */
10584 bool fImplicitDiffs = false;
10585 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10586 it != mMediaData->mAttachments.end();
10587 ++it)
10588 {
10589 const ComObjPtr<MediumAttachment> &pAtt = *it;
10590 if (pAtt->isImplicit())
10591 {
10592 fImplicitDiffs = true;
10593 break;
10594 }
10595 }
10596 /* If there is nothing to do, leave early. This saves lots of image locking
10597 * effort. It also avoids a MachineStateChanged event without real reason.
10598 * This is important e.g. when loading a VM config, because there should be
10599 * no events. Otherwise API clients can become thoroughly confused for
10600 * inaccessible VMs (the code for loading VM configs uses this method for
10601 * cleanup if the config makes no sense), as they take such events as an
10602 * indication that the VM is alive, and they would force the VM config to
10603 * be reread, leading to an endless loop. */
10604 if (!fImplicitDiffs)
10605 return S_OK;
10606
10607 HRESULT rc = S_OK;
10608 MachineState_T oldState = mData->mMachineState;
10609
10610 /* will release the lock before the potentially lengthy operation,
10611 * so protect with the special state (unless already protected) */
10612 if ( oldState != MachineState_Saving
10613 && oldState != MachineState_LiveSnapshotting
10614 && oldState != MachineState_RestoringSnapshot
10615 && oldState != MachineState_DeletingSnapshot
10616 && oldState != MachineState_DeletingSnapshotOnline
10617 && oldState != MachineState_DeletingSnapshotPaused
10618 )
10619 setMachineState(MachineState_SettingUp);
10620
10621 // use appropriate locked media map (online or offline)
10622 MediumLockListMap lockedMediaOffline;
10623 MediumLockListMap *lockedMediaMap;
10624 if (aOnline)
10625 lockedMediaMap = &mData->mSession.mLockedMedia;
10626 else
10627 lockedMediaMap = &lockedMediaOffline;
10628
10629 try
10630 {
10631 if (!aOnline)
10632 {
10633 /* lock all attached hard disks early to detect "in use"
10634 * situations before deleting actual diffs */
10635 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10636 it != mMediaData->mAttachments.end();
10637 ++it)
10638 {
10639 MediumAttachment* pAtt = *it;
10640 if (pAtt->getType() == DeviceType_HardDisk)
10641 {
10642 Medium* pMedium = pAtt->getMedium();
10643 Assert(pMedium);
10644
10645 MediumLockList *pMediumLockList(new MediumLockList());
10646 alock.release();
10647 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10648 false /* fMediumLockWrite */,
10649 NULL,
10650 *pMediumLockList);
10651 alock.acquire();
10652
10653 if (FAILED(rc))
10654 {
10655 delete pMediumLockList;
10656 throw rc;
10657 }
10658
10659 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10660 if (FAILED(rc))
10661 throw rc;
10662 }
10663 }
10664
10665 if (FAILED(rc))
10666 throw rc;
10667 } // end of offline
10668
10669 /* Lock lists are now up to date and include implicitly created media */
10670
10671 /* Go through remembered attachments and delete all implicitly created
10672 * diffs and fix up the attachment information */
10673 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10674 MediaData::AttachmentList implicitAtts;
10675 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10676 it != mMediaData->mAttachments.end();
10677 ++it)
10678 {
10679 ComObjPtr<MediumAttachment> pAtt = *it;
10680 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10681 if (pMedium.isNull())
10682 continue;
10683
10684 // Implicit attachments go on the list for deletion and back references are removed.
10685 if (pAtt->isImplicit())
10686 {
10687 /* Deassociate and mark for deletion */
10688 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10689 rc = pMedium->removeBackReference(mData->mUuid);
10690 if (FAILED(rc))
10691 throw rc;
10692 implicitAtts.push_back(pAtt);
10693 continue;
10694 }
10695
10696 /* Was this medium attached before? */
10697 if (!findAttachment(oldAtts, pMedium))
10698 {
10699 /* no: de-associate */
10700 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10701 rc = pMedium->removeBackReference(mData->mUuid);
10702 if (FAILED(rc))
10703 throw rc;
10704 continue;
10705 }
10706 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10707 }
10708
10709 /* If there are implicit attachments to delete, throw away the lock
10710 * map contents (which will unlock all media) since the medium
10711 * attachments will be rolled back. Below we need to completely
10712 * recreate the lock map anyway since it is infinitely complex to
10713 * do this incrementally (would need reconstructing each attachment
10714 * change, which would be extremely hairy). */
10715 if (implicitAtts.size() != 0)
10716 {
10717 ErrorInfoKeeper eik;
10718
10719 HRESULT rc1 = lockedMediaMap->Clear();
10720 AssertComRC(rc1);
10721 }
10722
10723 /* rollback hard disk changes */
10724 mMediaData.rollback();
10725
10726 MultiResult mrc(S_OK);
10727
10728 // Delete unused implicit diffs.
10729 if (implicitAtts.size() != 0)
10730 {
10731 alock.release();
10732
10733 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10734 it != implicitAtts.end();
10735 ++it)
10736 {
10737 // Remove medium associated with this attachment.
10738 ComObjPtr<MediumAttachment> pAtt = *it;
10739 Assert(pAtt);
10740 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10741 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10742 Assert(pMedium);
10743
10744 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10745 // continue on delete failure, just collect error messages
10746 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10747 mrc = rc;
10748 }
10749
10750 alock.acquire();
10751
10752 /* if there is a VM recreate media lock map as mentioned above,
10753 * otherwise it is a waste of time and we leave things unlocked */
10754 if (aOnline)
10755 {
10756 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10757 /* must never be NULL, but better safe than sorry */
10758 if (!pMachine.isNull())
10759 {
10760 alock.release();
10761 rc = mData->mSession.mMachine->lockMedia();
10762 alock.acquire();
10763 if (FAILED(rc))
10764 throw rc;
10765 }
10766 }
10767 }
10768 }
10769 catch (HRESULT aRC) {rc = aRC;}
10770
10771 if (mData->mMachineState == MachineState_SettingUp)
10772 setMachineState(oldState);
10773
10774 /* unlock all hard disks we locked when there is no VM */
10775 if (!aOnline)
10776 {
10777 ErrorInfoKeeper eik;
10778
10779 HRESULT rc1 = lockedMediaMap->Clear();
10780 AssertComRC(rc1);
10781 }
10782
10783 return rc;
10784}
10785
10786
10787/**
10788 * Looks through the given list of media attachments for one with the given parameters
10789 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10790 * can be searched as well if needed.
10791 *
10792 * @param list
10793 * @param aControllerName
10794 * @param aControllerPort
10795 * @param aDevice
10796 * @return
10797 */
10798MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10799 IN_BSTR aControllerName,
10800 LONG aControllerPort,
10801 LONG aDevice)
10802{
10803 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10804 it != ll.end();
10805 ++it)
10806 {
10807 MediumAttachment *pAttach = *it;
10808 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10809 return pAttach;
10810 }
10811
10812 return NULL;
10813}
10814
10815/**
10816 * Looks through the given list of media attachments for one with the given parameters
10817 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10818 * can be searched as well if needed.
10819 *
10820 * @param list
10821 * @param aControllerName
10822 * @param aControllerPort
10823 * @param aDevice
10824 * @return
10825 */
10826MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10827 ComObjPtr<Medium> pMedium)
10828{
10829 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10830 it != ll.end();
10831 ++it)
10832 {
10833 MediumAttachment *pAttach = *it;
10834 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10835 if (pMediumThis == pMedium)
10836 return pAttach;
10837 }
10838
10839 return NULL;
10840}
10841
10842/**
10843 * Looks through the given list of media attachments for one with the given parameters
10844 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10845 * can be searched as well if needed.
10846 *
10847 * @param list
10848 * @param aControllerName
10849 * @param aControllerPort
10850 * @param aDevice
10851 * @return
10852 */
10853MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10854 Guid &id)
10855{
10856 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10857 it != ll.end();
10858 ++it)
10859 {
10860 MediumAttachment *pAttach = *it;
10861 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10862 if (pMediumThis->getId() == id)
10863 return pAttach;
10864 }
10865
10866 return NULL;
10867}
10868
10869/**
10870 * Main implementation for Machine::DetachDevice. This also gets called
10871 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10872 *
10873 * @param pAttach Medium attachment to detach.
10874 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10875 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10876 * @return
10877 */
10878HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10879 AutoWriteLock &writeLock,
10880 Snapshot *pSnapshot)
10881{
10882 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10883 DeviceType_T mediumType = pAttach->getType();
10884
10885 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10886
10887 if (pAttach->isImplicit())
10888 {
10889 /* attempt to implicitly delete the implicitly created diff */
10890
10891 /// @todo move the implicit flag from MediumAttachment to Medium
10892 /// and forbid any hard disk operation when it is implicit. Or maybe
10893 /// a special media state for it to make it even more simple.
10894
10895 Assert(mMediaData.isBackedUp());
10896
10897 /* will release the lock before the potentially lengthy operation, so
10898 * protect with the special state */
10899 MachineState_T oldState = mData->mMachineState;
10900 setMachineState(MachineState_SettingUp);
10901
10902 writeLock.release();
10903
10904 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10905 true /*aWait*/);
10906
10907 writeLock.acquire();
10908
10909 setMachineState(oldState);
10910
10911 if (FAILED(rc)) return rc;
10912 }
10913
10914 setModified(IsModified_Storage);
10915 mMediaData.backup();
10916 mMediaData->mAttachments.remove(pAttach);
10917
10918 if (!oldmedium.isNull())
10919 {
10920 // if this is from a snapshot, do not defer detachment to commitMedia()
10921 if (pSnapshot)
10922 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10923 // else if non-hard disk media, do not defer detachment to commitMedia() either
10924 else if (mediumType != DeviceType_HardDisk)
10925 oldmedium->removeBackReference(mData->mUuid);
10926 }
10927
10928 return S_OK;
10929}
10930
10931/**
10932 * Goes thru all media of the given list and
10933 *
10934 * 1) calls detachDevice() on each of them for this machine and
10935 * 2) adds all Medium objects found in the process to the given list,
10936 * depending on cleanupMode.
10937 *
10938 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10939 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10940 * media to the list.
10941 *
10942 * This gets called from Machine::Unregister, both for the actual Machine and
10943 * the SnapshotMachine objects that might be found in the snapshots.
10944 *
10945 * Requires caller and locking. The machine lock must be passed in because it
10946 * will be passed on to detachDevice which needs it for temporary unlocking.
10947 *
10948 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
10949 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
10950 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
10951 * otherwise no media get added.
10952 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
10953 * @return
10954 */
10955HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
10956 Snapshot *pSnapshot,
10957 CleanupMode_T cleanupMode,
10958 MediaList &llMedia)
10959{
10960 Assert(isWriteLockOnCurrentThread());
10961
10962 HRESULT rc;
10963
10964 // make a temporary list because detachDevice invalidates iterators into
10965 // mMediaData->mAttachments
10966 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
10967
10968 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
10969 it != llAttachments2.end();
10970 ++it)
10971 {
10972 ComObjPtr<MediumAttachment> &pAttach = *it;
10973 ComObjPtr<Medium> pMedium = pAttach->getMedium();
10974
10975 if (!pMedium.isNull())
10976 {
10977 AutoCaller mac(pMedium);
10978 if (FAILED(mac.rc())) return mac.rc();
10979 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
10980 DeviceType_T devType = pMedium->getDeviceType();
10981 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
10982 && devType == DeviceType_HardDisk)
10983 || (cleanupMode == CleanupMode_Full)
10984 )
10985 {
10986 llMedia.push_back(pMedium);
10987 ComObjPtr<Medium> pParent = pMedium->getParent();
10988 /*
10989 * Search for medias which are not attached to any machine, but
10990 * in the chain to an attached disk. Mediums are only consided
10991 * if they are:
10992 * - have only one child
10993 * - no references to any machines
10994 * - are of normal medium type
10995 */
10996 while (!pParent.isNull())
10997 {
10998 AutoCaller mac1(pParent);
10999 if (FAILED(mac1.rc())) return mac1.rc();
11000 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11001 if (pParent->getChildren().size() == 1)
11002 {
11003 if ( pParent->getMachineBackRefCount() == 0
11004 && pParent->getType() == MediumType_Normal
11005 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11006 llMedia.push_back(pParent);
11007 }
11008 else
11009 break;
11010 pParent = pParent->getParent();
11011 }
11012 }
11013 }
11014
11015 // real machine: then we need to use the proper method
11016 rc = detachDevice(pAttach, writeLock, pSnapshot);
11017
11018 if (FAILED(rc))
11019 return rc;
11020 }
11021
11022 return S_OK;
11023}
11024
11025/**
11026 * Perform deferred hard disk detachments.
11027 *
11028 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11029 * backed up).
11030 *
11031 * If @a aOnline is @c true then this method will also unlock the old hard disks
11032 * for which the new implicit diffs were created and will lock these new diffs for
11033 * writing.
11034 *
11035 * @param aOnline Whether the VM was online prior to this operation.
11036 *
11037 * @note Locks this object for writing!
11038 */
11039void Machine::commitMedia(bool aOnline /*= false*/)
11040{
11041 AutoCaller autoCaller(this);
11042 AssertComRCReturnVoid(autoCaller.rc());
11043
11044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11045
11046 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11047
11048 HRESULT rc = S_OK;
11049
11050 /* no attach/detach operations -- nothing to do */
11051 if (!mMediaData.isBackedUp())
11052 return;
11053
11054 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11055 bool fMediaNeedsLocking = false;
11056
11057 /* enumerate new attachments */
11058 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11059 it != mMediaData->mAttachments.end();
11060 ++it)
11061 {
11062 MediumAttachment *pAttach = *it;
11063
11064 pAttach->commit();
11065
11066 Medium* pMedium = pAttach->getMedium();
11067 bool fImplicit = pAttach->isImplicit();
11068
11069 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11070 (pMedium) ? pMedium->getName().c_str() : "NULL",
11071 fImplicit));
11072
11073 /** @todo convert all this Machine-based voodoo to MediumAttachment
11074 * based commit logic. */
11075 if (fImplicit)
11076 {
11077 /* convert implicit attachment to normal */
11078 pAttach->setImplicit(false);
11079
11080 if ( aOnline
11081 && pMedium
11082 && pAttach->getType() == DeviceType_HardDisk
11083 )
11084 {
11085 ComObjPtr<Medium> parent = pMedium->getParent();
11086 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11087
11088 /* update the appropriate lock list */
11089 MediumLockList *pMediumLockList;
11090 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11091 AssertComRC(rc);
11092 if (pMediumLockList)
11093 {
11094 /* unlock if there's a need to change the locking */
11095 if (!fMediaNeedsLocking)
11096 {
11097 rc = mData->mSession.mLockedMedia.Unlock();
11098 AssertComRC(rc);
11099 fMediaNeedsLocking = true;
11100 }
11101 rc = pMediumLockList->Update(parent, false);
11102 AssertComRC(rc);
11103 rc = pMediumLockList->Append(pMedium, true);
11104 AssertComRC(rc);
11105 }
11106 }
11107
11108 continue;
11109 }
11110
11111 if (pMedium)
11112 {
11113 /* was this medium attached before? */
11114 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11115 oldIt != oldAtts.end();
11116 ++oldIt)
11117 {
11118 MediumAttachment *pOldAttach = *oldIt;
11119 if (pOldAttach->getMedium() == pMedium)
11120 {
11121 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11122
11123 /* yes: remove from old to avoid de-association */
11124 oldAtts.erase(oldIt);
11125 break;
11126 }
11127 }
11128 }
11129 }
11130
11131 /* enumerate remaining old attachments and de-associate from the
11132 * current machine state */
11133 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11134 it != oldAtts.end();
11135 ++it)
11136 {
11137 MediumAttachment *pAttach = *it;
11138 Medium* pMedium = pAttach->getMedium();
11139
11140 /* Detach only hard disks, since DVD/floppy media is detached
11141 * instantly in MountMedium. */
11142 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11143 {
11144 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11145
11146 /* now de-associate from the current machine state */
11147 rc = pMedium->removeBackReference(mData->mUuid);
11148 AssertComRC(rc);
11149
11150 if (aOnline)
11151 {
11152 /* unlock since medium is not used anymore */
11153 MediumLockList *pMediumLockList;
11154 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11155 AssertComRC(rc);
11156 if (pMediumLockList)
11157 {
11158 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11159 AssertComRC(rc);
11160 }
11161 }
11162 }
11163 }
11164
11165 /* take media locks again so that the locking state is consistent */
11166 if (fMediaNeedsLocking)
11167 {
11168 Assert(aOnline);
11169 rc = mData->mSession.mLockedMedia.Lock();
11170 AssertComRC(rc);
11171 }
11172
11173 /* commit the hard disk changes */
11174 mMediaData.commit();
11175
11176 if (isSessionMachine())
11177 {
11178 /*
11179 * Update the parent machine to point to the new owner.
11180 * This is necessary because the stored parent will point to the
11181 * session machine otherwise and cause crashes or errors later
11182 * when the session machine gets invalid.
11183 */
11184 /** @todo Change the MediumAttachment class to behave like any other
11185 * class in this regard by creating peer MediumAttachment
11186 * objects for session machines and share the data with the peer
11187 * machine.
11188 */
11189 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11190 it != mMediaData->mAttachments.end();
11191 ++it)
11192 {
11193 (*it)->updateParentMachine(mPeer);
11194 }
11195
11196 /* attach new data to the primary machine and reshare it */
11197 mPeer->mMediaData.attach(mMediaData);
11198 }
11199
11200 return;
11201}
11202
11203/**
11204 * Perform deferred deletion of implicitly created diffs.
11205 *
11206 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11207 * backed up).
11208 *
11209 * @note Locks this object for writing!
11210 */
11211void Machine::rollbackMedia()
11212{
11213 AutoCaller autoCaller(this);
11214 AssertComRCReturnVoid(autoCaller.rc());
11215
11216 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11217 LogFlowThisFunc(("Entering rollbackMedia\n"));
11218
11219 HRESULT rc = S_OK;
11220
11221 /* no attach/detach operations -- nothing to do */
11222 if (!mMediaData.isBackedUp())
11223 return;
11224
11225 /* enumerate new attachments */
11226 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11227 it != mMediaData->mAttachments.end();
11228 ++it)
11229 {
11230 MediumAttachment *pAttach = *it;
11231 /* Fix up the backrefs for DVD/floppy media. */
11232 if (pAttach->getType() != DeviceType_HardDisk)
11233 {
11234 Medium* pMedium = pAttach->getMedium();
11235 if (pMedium)
11236 {
11237 rc = pMedium->removeBackReference(mData->mUuid);
11238 AssertComRC(rc);
11239 }
11240 }
11241
11242 (*it)->rollback();
11243
11244 pAttach = *it;
11245 /* Fix up the backrefs for DVD/floppy media. */
11246 if (pAttach->getType() != DeviceType_HardDisk)
11247 {
11248 Medium* pMedium = pAttach->getMedium();
11249 if (pMedium)
11250 {
11251 rc = pMedium->addBackReference(mData->mUuid);
11252 AssertComRC(rc);
11253 }
11254 }
11255 }
11256
11257 /** @todo convert all this Machine-based voodoo to MediumAttachment
11258 * based rollback logic. */
11259 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11260
11261 return;
11262}
11263
11264/**
11265 * Returns true if the settings file is located in the directory named exactly
11266 * as the machine; this means, among other things, that the machine directory
11267 * should be auto-renamed.
11268 *
11269 * @param aSettingsDir if not NULL, the full machine settings file directory
11270 * name will be assigned there.
11271 *
11272 * @note Doesn't lock anything.
11273 * @note Not thread safe (must be called from this object's lock).
11274 */
11275bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11276{
11277 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11278 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11279 if (aSettingsDir)
11280 *aSettingsDir = strMachineDirName;
11281 strMachineDirName.stripPath(); // vmname
11282 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11283 strConfigFileOnly.stripPath() // vmname.vbox
11284 .stripExt(); // vmname
11285 /** @todo hack, make somehow use of ComposeMachineFilename */
11286 if (mUserData->s.fDirectoryIncludesUUID)
11287 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11288
11289 AssertReturn(!strMachineDirName.isEmpty(), false);
11290 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11291
11292 return strMachineDirName == strConfigFileOnly;
11293}
11294
11295/**
11296 * Discards all changes to machine settings.
11297 *
11298 * @param aNotify Whether to notify the direct session about changes or not.
11299 *
11300 * @note Locks objects for writing!
11301 */
11302void Machine::rollback(bool aNotify)
11303{
11304 AutoCaller autoCaller(this);
11305 AssertComRCReturn(autoCaller.rc(), (void)0);
11306
11307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11308
11309 if (!mStorageControllers.isNull())
11310 {
11311 if (mStorageControllers.isBackedUp())
11312 {
11313 /* unitialize all new devices (absent in the backed up list). */
11314 StorageControllerList::const_iterator it = mStorageControllers->begin();
11315 StorageControllerList *backedList = mStorageControllers.backedUpData();
11316 while (it != mStorageControllers->end())
11317 {
11318 if ( std::find(backedList->begin(), backedList->end(), *it)
11319 == backedList->end()
11320 )
11321 {
11322 (*it)->uninit();
11323 }
11324 ++it;
11325 }
11326
11327 /* restore the list */
11328 mStorageControllers.rollback();
11329 }
11330
11331 /* rollback any changes to devices after restoring the list */
11332 if (mData->flModifications & IsModified_Storage)
11333 {
11334 StorageControllerList::const_iterator it = mStorageControllers->begin();
11335 while (it != mStorageControllers->end())
11336 {
11337 (*it)->rollback();
11338 ++it;
11339 }
11340 }
11341 }
11342
11343 mUserData.rollback();
11344
11345 mHWData.rollback();
11346
11347 if (mData->flModifications & IsModified_Storage)
11348 rollbackMedia();
11349
11350 if (mBIOSSettings)
11351 mBIOSSettings->rollback();
11352
11353 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11354 mVRDEServer->rollback();
11355
11356 if (mAudioAdapter)
11357 mAudioAdapter->rollback();
11358
11359 if (mUSBController && (mData->flModifications & IsModified_USB))
11360 mUSBController->rollback();
11361
11362 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11363 mBandwidthControl->rollback();
11364
11365 if (!mHWData.isNull())
11366 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11367 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11368 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11369 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11370
11371 if (mData->flModifications & IsModified_NetworkAdapters)
11372 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11373 if ( mNetworkAdapters[slot]
11374 && mNetworkAdapters[slot]->isModified())
11375 {
11376 mNetworkAdapters[slot]->rollback();
11377 networkAdapters[slot] = mNetworkAdapters[slot];
11378 }
11379
11380 if (mData->flModifications & IsModified_SerialPorts)
11381 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11382 if ( mSerialPorts[slot]
11383 && mSerialPorts[slot]->isModified())
11384 {
11385 mSerialPorts[slot]->rollback();
11386 serialPorts[slot] = mSerialPorts[slot];
11387 }
11388
11389 if (mData->flModifications & IsModified_ParallelPorts)
11390 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11391 if ( mParallelPorts[slot]
11392 && mParallelPorts[slot]->isModified())
11393 {
11394 mParallelPorts[slot]->rollback();
11395 parallelPorts[slot] = mParallelPorts[slot];
11396 }
11397
11398 if (aNotify)
11399 {
11400 /* inform the direct session about changes */
11401
11402 ComObjPtr<Machine> that = this;
11403 uint32_t flModifications = mData->flModifications;
11404 alock.release();
11405
11406 if (flModifications & IsModified_SharedFolders)
11407 that->onSharedFolderChange();
11408
11409 if (flModifications & IsModified_VRDEServer)
11410 that->onVRDEServerChange(/* aRestart */ TRUE);
11411 if (flModifications & IsModified_USB)
11412 that->onUSBControllerChange();
11413
11414 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11415 if (networkAdapters[slot])
11416 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11417 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11418 if (serialPorts[slot])
11419 that->onSerialPortChange(serialPorts[slot]);
11420 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11421 if (parallelPorts[slot])
11422 that->onParallelPortChange(parallelPorts[slot]);
11423
11424 if (flModifications & IsModified_Storage)
11425 that->onStorageControllerChange();
11426
11427#if 0
11428 if (flModifications & IsModified_BandwidthControl)
11429 that->onBandwidthControlChange();
11430#endif
11431 }
11432}
11433
11434/**
11435 * Commits all the changes to machine settings.
11436 *
11437 * Note that this operation is supposed to never fail.
11438 *
11439 * @note Locks this object and children for writing.
11440 */
11441void Machine::commit()
11442{
11443 AutoCaller autoCaller(this);
11444 AssertComRCReturnVoid(autoCaller.rc());
11445
11446 AutoCaller peerCaller(mPeer);
11447 AssertComRCReturnVoid(peerCaller.rc());
11448
11449 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11450
11451 /*
11452 * use safe commit to ensure Snapshot machines (that share mUserData)
11453 * will still refer to a valid memory location
11454 */
11455 mUserData.commitCopy();
11456
11457 mHWData.commit();
11458
11459 if (mMediaData.isBackedUp())
11460 commitMedia(Global::IsOnline(mData->mMachineState));
11461
11462 mBIOSSettings->commit();
11463 mVRDEServer->commit();
11464 mAudioAdapter->commit();
11465 mUSBController->commit();
11466 mBandwidthControl->commit();
11467
11468 /* Since mNetworkAdapters is a list which might have been changed (resized)
11469 * without using the Backupable<> template we need to handle the copying
11470 * of the list entries manually, including the creation of peers for the
11471 * new objects. */
11472 bool commitNetworkAdapters = false;
11473 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11474 if (mPeer)
11475 {
11476 /* commit everything, even the ones which will go away */
11477 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11478 mNetworkAdapters[slot]->commit();
11479 /* copy over the new entries, creating a peer and uninit the original */
11480 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11481 for (size_t slot = 0; slot < newSize; slot++)
11482 {
11483 /* look if this adapter has a peer device */
11484 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11485 if (!peer)
11486 {
11487 /* no peer means the adapter is a newly created one;
11488 * create a peer owning data this data share it with */
11489 peer.createObject();
11490 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11491 }
11492 mPeer->mNetworkAdapters[slot] = peer;
11493 }
11494 /* uninit any no longer needed network adapters */
11495 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11496 mNetworkAdapters[slot]->uninit();
11497 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11498 {
11499 if (mPeer->mNetworkAdapters[slot])
11500 mPeer->mNetworkAdapters[slot]->uninit();
11501 }
11502 /* Keep the original network adapter count until this point, so that
11503 * discarding a chipset type change will not lose settings. */
11504 mNetworkAdapters.resize(newSize);
11505 mPeer->mNetworkAdapters.resize(newSize);
11506 }
11507 else
11508 {
11509 /* we have no peer (our parent is the newly created machine);
11510 * just commit changes to the network adapters */
11511 commitNetworkAdapters = true;
11512 }
11513 if (commitNetworkAdapters)
11514 {
11515 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11516 mNetworkAdapters[slot]->commit();
11517 }
11518
11519 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11520 mSerialPorts[slot]->commit();
11521 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11522 mParallelPorts[slot]->commit();
11523
11524 bool commitStorageControllers = false;
11525
11526 if (mStorageControllers.isBackedUp())
11527 {
11528 mStorageControllers.commit();
11529
11530 if (mPeer)
11531 {
11532 /* Commit all changes to new controllers (this will reshare data with
11533 * peers for those who have peers) */
11534 StorageControllerList *newList = new StorageControllerList();
11535 StorageControllerList::const_iterator it = mStorageControllers->begin();
11536 while (it != mStorageControllers->end())
11537 {
11538 (*it)->commit();
11539
11540 /* look if this controller has a peer device */
11541 ComObjPtr<StorageController> peer = (*it)->getPeer();
11542 if (!peer)
11543 {
11544 /* no peer means the device is a newly created one;
11545 * create a peer owning data this device share it with */
11546 peer.createObject();
11547 peer->init(mPeer, *it, true /* aReshare */);
11548 }
11549 else
11550 {
11551 /* remove peer from the old list */
11552 mPeer->mStorageControllers->remove(peer);
11553 }
11554 /* and add it to the new list */
11555 newList->push_back(peer);
11556
11557 ++it;
11558 }
11559
11560 /* uninit old peer's controllers that are left */
11561 it = mPeer->mStorageControllers->begin();
11562 while (it != mPeer->mStorageControllers->end())
11563 {
11564 (*it)->uninit();
11565 ++it;
11566 }
11567
11568 /* attach new list of controllers to our peer */
11569 mPeer->mStorageControllers.attach(newList);
11570 }
11571 else
11572 {
11573 /* we have no peer (our parent is the newly created machine);
11574 * just commit changes to devices */
11575 commitStorageControllers = true;
11576 }
11577 }
11578 else
11579 {
11580 /* the list of controllers itself is not changed,
11581 * just commit changes to controllers themselves */
11582 commitStorageControllers = true;
11583 }
11584
11585 if (commitStorageControllers)
11586 {
11587 StorageControllerList::const_iterator it = mStorageControllers->begin();
11588 while (it != mStorageControllers->end())
11589 {
11590 (*it)->commit();
11591 ++it;
11592 }
11593 }
11594
11595 if (isSessionMachine())
11596 {
11597 /* attach new data to the primary machine and reshare it */
11598 mPeer->mUserData.attach(mUserData);
11599 mPeer->mHWData.attach(mHWData);
11600 /* mMediaData is reshared by fixupMedia */
11601 // mPeer->mMediaData.attach(mMediaData);
11602 Assert(mPeer->mMediaData.data() == mMediaData.data());
11603 }
11604}
11605
11606/**
11607 * Copies all the hardware data from the given machine.
11608 *
11609 * Currently, only called when the VM is being restored from a snapshot. In
11610 * particular, this implies that the VM is not running during this method's
11611 * call.
11612 *
11613 * @note This method must be called from under this object's lock.
11614 *
11615 * @note This method doesn't call #commit(), so all data remains backed up and
11616 * unsaved.
11617 */
11618void Machine::copyFrom(Machine *aThat)
11619{
11620 AssertReturnVoid(!isSnapshotMachine());
11621 AssertReturnVoid(aThat->isSnapshotMachine());
11622
11623 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11624
11625 mHWData.assignCopy(aThat->mHWData);
11626
11627 // create copies of all shared folders (mHWData after attaching a copy
11628 // contains just references to original objects)
11629 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11630 it != mHWData->mSharedFolders.end();
11631 ++it)
11632 {
11633 ComObjPtr<SharedFolder> folder;
11634 folder.createObject();
11635 HRESULT rc = folder->initCopy(getMachine(), *it);
11636 AssertComRC(rc);
11637 *it = folder;
11638 }
11639
11640 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11641 mVRDEServer->copyFrom(aThat->mVRDEServer);
11642 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11643 mUSBController->copyFrom(aThat->mUSBController);
11644 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11645
11646 /* create private copies of all controllers */
11647 mStorageControllers.backup();
11648 mStorageControllers->clear();
11649 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11650 it != aThat->mStorageControllers->end();
11651 ++it)
11652 {
11653 ComObjPtr<StorageController> ctrl;
11654 ctrl.createObject();
11655 ctrl->initCopy(this, *it);
11656 mStorageControllers->push_back(ctrl);
11657 }
11658
11659 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11660 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11661 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11662 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11663 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11664 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11665 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11666}
11667
11668/**
11669 * Returns whether the given storage controller is hotplug capable.
11670 *
11671 * @returns true if the controller supports hotplugging
11672 * false otherwise.
11673 * @param enmCtrlType The controller type to check for.
11674 */
11675bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11676{
11677 switch (enmCtrlType)
11678 {
11679 case StorageControllerType_IntelAhci:
11680 return true;
11681 case StorageControllerType_LsiLogic:
11682 case StorageControllerType_LsiLogicSas:
11683 case StorageControllerType_BusLogic:
11684 case StorageControllerType_PIIX3:
11685 case StorageControllerType_PIIX4:
11686 case StorageControllerType_ICH6:
11687 case StorageControllerType_I82078:
11688 default:
11689 return false;
11690 }
11691}
11692
11693#ifdef VBOX_WITH_RESOURCE_USAGE_API
11694
11695void Machine::getDiskList(MediaList &list)
11696{
11697 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11698 it != mMediaData->mAttachments.end();
11699 ++it)
11700 {
11701 MediumAttachment* pAttach = *it;
11702 /* just in case */
11703 AssertStmt(pAttach, continue);
11704
11705 AutoCaller localAutoCallerA(pAttach);
11706 if (FAILED(localAutoCallerA.rc())) continue;
11707
11708 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11709
11710 if (pAttach->getType() == DeviceType_HardDisk)
11711 list.push_back(pAttach->getMedium());
11712 }
11713}
11714
11715void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11716{
11717 AssertReturnVoid(isWriteLockOnCurrentThread());
11718 AssertPtrReturnVoid(aCollector);
11719
11720 pm::CollectorHAL *hal = aCollector->getHAL();
11721 /* Create sub metrics */
11722 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11723 "Percentage of processor time spent in user mode by the VM process.");
11724 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11725 "Percentage of processor time spent in kernel mode by the VM process.");
11726 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11727 "Size of resident portion of VM process in memory.");
11728 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11729 "Actual size of all VM disks combined.");
11730 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11731 "Network receive rate.");
11732 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11733 "Network transmit rate.");
11734 /* Create and register base metrics */
11735 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11736 cpuLoadUser, cpuLoadKernel);
11737 aCollector->registerBaseMetric(cpuLoad);
11738 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11739 ramUsageUsed);
11740 aCollector->registerBaseMetric(ramUsage);
11741 MediaList disks;
11742 getDiskList(disks);
11743 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11744 diskUsageUsed);
11745 aCollector->registerBaseMetric(diskUsage);
11746
11747 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11748 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11749 new pm::AggregateAvg()));
11750 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11751 new pm::AggregateMin()));
11752 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11753 new pm::AggregateMax()));
11754 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11755 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11756 new pm::AggregateAvg()));
11757 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11758 new pm::AggregateMin()));
11759 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11760 new pm::AggregateMax()));
11761
11762 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11763 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11764 new pm::AggregateAvg()));
11765 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11766 new pm::AggregateMin()));
11767 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11768 new pm::AggregateMax()));
11769
11770 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11771 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11772 new pm::AggregateAvg()));
11773 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11774 new pm::AggregateMin()));
11775 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11776 new pm::AggregateMax()));
11777
11778
11779 /* Guest metrics collector */
11780 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11781 aCollector->registerGuest(mCollectorGuest);
11782 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11783 this, __PRETTY_FUNCTION__, mCollectorGuest));
11784
11785 /* Create sub metrics */
11786 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11787 "Percentage of processor time spent in user mode as seen by the guest.");
11788 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11789 "Percentage of processor time spent in kernel mode as seen by the guest.");
11790 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11791 "Percentage of processor time spent idling as seen by the guest.");
11792
11793 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11794 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11795 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11796 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11797 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11798 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11799
11800 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11801
11802 /* Create and register base metrics */
11803 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11804 machineNetRx, machineNetTx);
11805 aCollector->registerBaseMetric(machineNetRate);
11806
11807 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11808 guestLoadUser, guestLoadKernel, guestLoadIdle);
11809 aCollector->registerBaseMetric(guestCpuLoad);
11810
11811 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11812 guestMemTotal, guestMemFree,
11813 guestMemBalloon, guestMemShared,
11814 guestMemCache, guestPagedTotal);
11815 aCollector->registerBaseMetric(guestCpuMem);
11816
11817 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11818 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11819 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11820 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11821
11822 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11823 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11824 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11825 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11826
11827 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11828 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11829 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11830 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11831
11832 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11833 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11834 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11835 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11836
11837 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11838 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11839 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11840 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11841
11842 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11843 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11844 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11845 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11846
11847 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11848 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11849 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11850 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11851
11852 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11853 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11854 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11855 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11856
11857 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11858 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11859 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11860 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11861
11862 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11863 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11864 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11865 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11866
11867 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11868 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11869 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11870 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11871}
11872
11873void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11874{
11875 AssertReturnVoid(isWriteLockOnCurrentThread());
11876
11877 if (aCollector)
11878 {
11879 aCollector->unregisterMetricsFor(aMachine);
11880 aCollector->unregisterBaseMetricsFor(aMachine);
11881 }
11882}
11883
11884#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11885
11886
11887////////////////////////////////////////////////////////////////////////////////
11888
11889DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11890
11891HRESULT SessionMachine::FinalConstruct()
11892{
11893 LogFlowThisFunc(("\n"));
11894
11895#if defined(RT_OS_WINDOWS)
11896 mIPCSem = NULL;
11897#elif defined(RT_OS_OS2)
11898 mIPCSem = NULLHANDLE;
11899#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11900 mIPCSem = -1;
11901#else
11902# error "Port me!"
11903#endif
11904
11905 return BaseFinalConstruct();
11906}
11907
11908void SessionMachine::FinalRelease()
11909{
11910 LogFlowThisFunc(("\n"));
11911
11912 uninit(Uninit::Unexpected);
11913
11914 BaseFinalRelease();
11915}
11916
11917/**
11918 * @note Must be called only by Machine::openSession() from its own write lock.
11919 */
11920HRESULT SessionMachine::init(Machine *aMachine)
11921{
11922 LogFlowThisFuncEnter();
11923 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11924
11925 AssertReturn(aMachine, E_INVALIDARG);
11926
11927 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11928
11929 /* Enclose the state transition NotReady->InInit->Ready */
11930 AutoInitSpan autoInitSpan(this);
11931 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11932
11933 /* create the interprocess semaphore */
11934#if defined(RT_OS_WINDOWS)
11935 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11936 for (size_t i = 0; i < mIPCSemName.length(); i++)
11937 if (mIPCSemName.raw()[i] == '\\')
11938 mIPCSemName.raw()[i] = '/';
11939 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11940 ComAssertMsgRet(mIPCSem,
11941 ("Cannot create IPC mutex '%ls', err=%d",
11942 mIPCSemName.raw(), ::GetLastError()),
11943 E_FAIL);
11944#elif defined(RT_OS_OS2)
11945 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11946 aMachine->mData->mUuid.raw());
11947 mIPCSemName = ipcSem;
11948 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
11949 ComAssertMsgRet(arc == NO_ERROR,
11950 ("Cannot create IPC mutex '%s', arc=%ld",
11951 ipcSem.c_str(), arc),
11952 E_FAIL);
11953#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11954# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
11955# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
11956 /** @todo Check that this still works correctly. */
11957 AssertCompileSize(key_t, 8);
11958# else
11959 AssertCompileSize(key_t, 4);
11960# endif
11961 key_t key;
11962 mIPCSem = -1;
11963 mIPCKey = "0";
11964 for (uint32_t i = 0; i < 1 << 24; i++)
11965 {
11966 key = ((uint32_t)'V' << 24) | i;
11967 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
11968 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
11969 {
11970 mIPCSem = sem;
11971 if (sem >= 0)
11972 mIPCKey = BstrFmt("%u", key);
11973 break;
11974 }
11975 }
11976# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11977 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
11978 char *pszSemName = NULL;
11979 RTStrUtf8ToCurrentCP(&pszSemName, semName);
11980 key_t key = ::ftok(pszSemName, 'V');
11981 RTStrFree(pszSemName);
11982
11983 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
11984# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
11985
11986 int errnoSave = errno;
11987 if (mIPCSem < 0 && errnoSave == ENOSYS)
11988 {
11989 setError(E_FAIL,
11990 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
11991 "support for SysV IPC. Check the host kernel configuration for "
11992 "CONFIG_SYSVIPC=y"));
11993 return E_FAIL;
11994 }
11995 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
11996 * the IPC semaphores */
11997 if (mIPCSem < 0 && errnoSave == ENOSPC)
11998 {
11999#ifdef RT_OS_LINUX
12000 setError(E_FAIL,
12001 tr("Cannot create IPC semaphore because the system limit for the "
12002 "maximum number of semaphore sets (SEMMNI), or the system wide "
12003 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12004 "current set of SysV IPC semaphores can be determined from "
12005 "the file /proc/sysvipc/sem"));
12006#else
12007 setError(E_FAIL,
12008 tr("Cannot create IPC semaphore because the system-imposed limit "
12009 "on the maximum number of allowed semaphores or semaphore "
12010 "identifiers system-wide would be exceeded"));
12011#endif
12012 return E_FAIL;
12013 }
12014 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12015 E_FAIL);
12016 /* set the initial value to 1 */
12017 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12018 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12019 E_FAIL);
12020#else
12021# error "Port me!"
12022#endif
12023
12024 /* memorize the peer Machine */
12025 unconst(mPeer) = aMachine;
12026 /* share the parent pointer */
12027 unconst(mParent) = aMachine->mParent;
12028
12029 /* take the pointers to data to share */
12030 mData.share(aMachine->mData);
12031 mSSData.share(aMachine->mSSData);
12032
12033 mUserData.share(aMachine->mUserData);
12034 mHWData.share(aMachine->mHWData);
12035 mMediaData.share(aMachine->mMediaData);
12036
12037 mStorageControllers.allocate();
12038 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12039 it != aMachine->mStorageControllers->end();
12040 ++it)
12041 {
12042 ComObjPtr<StorageController> ctl;
12043 ctl.createObject();
12044 ctl->init(this, *it);
12045 mStorageControllers->push_back(ctl);
12046 }
12047
12048 unconst(mBIOSSettings).createObject();
12049 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12050 /* create another VRDEServer object that will be mutable */
12051 unconst(mVRDEServer).createObject();
12052 mVRDEServer->init(this, aMachine->mVRDEServer);
12053 /* create another audio adapter object that will be mutable */
12054 unconst(mAudioAdapter).createObject();
12055 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12056 /* create a list of serial ports that will be mutable */
12057 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12058 {
12059 unconst(mSerialPorts[slot]).createObject();
12060 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12061 }
12062 /* create a list of parallel ports that will be mutable */
12063 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12064 {
12065 unconst(mParallelPorts[slot]).createObject();
12066 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12067 }
12068 /* create another USB controller object that will be mutable */
12069 unconst(mUSBController).createObject();
12070 mUSBController->init(this, aMachine->mUSBController);
12071
12072 /* create a list of network adapters that will be mutable */
12073 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12074 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12075 {
12076 unconst(mNetworkAdapters[slot]).createObject();
12077 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12078 }
12079
12080 /* create another bandwidth control object that will be mutable */
12081 unconst(mBandwidthControl).createObject();
12082 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12083
12084 /* default is to delete saved state on Saved -> PoweredOff transition */
12085 mRemoveSavedState = true;
12086
12087 /* Confirm a successful initialization when it's the case */
12088 autoInitSpan.setSucceeded();
12089
12090 LogFlowThisFuncLeave();
12091 return S_OK;
12092}
12093
12094/**
12095 * Uninitializes this session object. If the reason is other than
12096 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12097 *
12098 * @param aReason uninitialization reason
12099 *
12100 * @note Locks mParent + this object for writing.
12101 */
12102void SessionMachine::uninit(Uninit::Reason aReason)
12103{
12104 LogFlowThisFuncEnter();
12105 LogFlowThisFunc(("reason=%d\n", aReason));
12106
12107 /*
12108 * Strongly reference ourselves to prevent this object deletion after
12109 * mData->mSession.mMachine.setNull() below (which can release the last
12110 * reference and call the destructor). Important: this must be done before
12111 * accessing any members (and before AutoUninitSpan that does it as well).
12112 * This self reference will be released as the very last step on return.
12113 */
12114 ComObjPtr<SessionMachine> selfRef = this;
12115
12116 /* Enclose the state transition Ready->InUninit->NotReady */
12117 AutoUninitSpan autoUninitSpan(this);
12118 if (autoUninitSpan.uninitDone())
12119 {
12120 LogFlowThisFunc(("Already uninitialized\n"));
12121 LogFlowThisFuncLeave();
12122 return;
12123 }
12124
12125 if (autoUninitSpan.initFailed())
12126 {
12127 /* We've been called by init() because it's failed. It's not really
12128 * necessary (nor it's safe) to perform the regular uninit sequence
12129 * below, the following is enough.
12130 */
12131 LogFlowThisFunc(("Initialization failed.\n"));
12132#if defined(RT_OS_WINDOWS)
12133 if (mIPCSem)
12134 ::CloseHandle(mIPCSem);
12135 mIPCSem = NULL;
12136#elif defined(RT_OS_OS2)
12137 if (mIPCSem != NULLHANDLE)
12138 ::DosCloseMutexSem(mIPCSem);
12139 mIPCSem = NULLHANDLE;
12140#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12141 if (mIPCSem >= 0)
12142 ::semctl(mIPCSem, 0, IPC_RMID);
12143 mIPCSem = -1;
12144# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12145 mIPCKey = "0";
12146# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12147#else
12148# error "Port me!"
12149#endif
12150 uninitDataAndChildObjects();
12151 mData.free();
12152 unconst(mParent) = NULL;
12153 unconst(mPeer) = NULL;
12154 LogFlowThisFuncLeave();
12155 return;
12156 }
12157
12158 MachineState_T lastState;
12159 {
12160 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12161 lastState = mData->mMachineState;
12162 }
12163 NOREF(lastState);
12164
12165#ifdef VBOX_WITH_USB
12166 // release all captured USB devices, but do this before requesting the locks below
12167 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12168 {
12169 /* Console::captureUSBDevices() is called in the VM process only after
12170 * setting the machine state to Starting or Restoring.
12171 * Console::detachAllUSBDevices() will be called upon successful
12172 * termination. So, we need to release USB devices only if there was
12173 * an abnormal termination of a running VM.
12174 *
12175 * This is identical to SessionMachine::DetachAllUSBDevices except
12176 * for the aAbnormal argument. */
12177 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12178 AssertComRC(rc);
12179 NOREF(rc);
12180
12181 USBProxyService *service = mParent->host()->usbProxyService();
12182 if (service)
12183 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12184 }
12185#endif /* VBOX_WITH_USB */
12186
12187 // we need to lock this object in uninit() because the lock is shared
12188 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12189 // and others need mParent lock, and USB needs host lock.
12190 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12191
12192#if 0
12193 // Trigger async cleanup tasks, avoid doing things here which are not
12194 // vital to be done immediately and maybe need more locks. This calls
12195 // Machine::unregisterMetrics().
12196 mParent->onMachineUninit(mPeer);
12197#else
12198 /*
12199 * It is safe to call Machine::unregisterMetrics() here because
12200 * PerformanceCollector::samplerCallback no longer accesses guest methods
12201 * holding the lock.
12202 */
12203 unregisterMetrics(mParent->performanceCollector(), mPeer);
12204#endif
12205 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12206 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12207 this, __PRETTY_FUNCTION__, mCollectorGuest));
12208 if (mCollectorGuest)
12209 {
12210 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12211 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12212 mCollectorGuest = NULL;
12213 }
12214
12215 if (aReason == Uninit::Abnormal)
12216 {
12217 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12218 Global::IsOnlineOrTransient(lastState)));
12219
12220 /* reset the state to Aborted */
12221 if (mData->mMachineState != MachineState_Aborted)
12222 setMachineState(MachineState_Aborted);
12223 }
12224
12225 // any machine settings modified?
12226 if (mData->flModifications)
12227 {
12228 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12229 rollback(false /* aNotify */);
12230 }
12231
12232 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12233 || !mConsoleTaskData.mSnapshot);
12234 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12235 {
12236 LogWarningThisFunc(("canceling failed save state request!\n"));
12237 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12238 }
12239 else if (!mConsoleTaskData.mSnapshot.isNull())
12240 {
12241 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12242
12243 /* delete all differencing hard disks created (this will also attach
12244 * their parents back by rolling back mMediaData) */
12245 rollbackMedia();
12246
12247 // delete the saved state file (it might have been already created)
12248 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12249 // think it's still in use
12250 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12251 mConsoleTaskData.mSnapshot->uninit();
12252 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12253 }
12254
12255 if (!mData->mSession.mType.isEmpty())
12256 {
12257 /* mType is not null when this machine's process has been started by
12258 * Machine::LaunchVMProcess(), therefore it is our child. We
12259 * need to queue the PID to reap the process (and avoid zombies on
12260 * Linux). */
12261 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12262 mParent->addProcessToReap(mData->mSession.mPID);
12263 }
12264
12265 mData->mSession.mPID = NIL_RTPROCESS;
12266
12267 if (aReason == Uninit::Unexpected)
12268 {
12269 /* Uninitialization didn't come from #checkForDeath(), so tell the
12270 * client watcher thread to update the set of machines that have open
12271 * sessions. */
12272 mParent->updateClientWatcher();
12273 }
12274
12275 /* uninitialize all remote controls */
12276 if (mData->mSession.mRemoteControls.size())
12277 {
12278 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12279 mData->mSession.mRemoteControls.size()));
12280
12281 Data::Session::RemoteControlList::iterator it =
12282 mData->mSession.mRemoteControls.begin();
12283 while (it != mData->mSession.mRemoteControls.end())
12284 {
12285 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12286 HRESULT rc = (*it)->Uninitialize();
12287 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12288 if (FAILED(rc))
12289 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12290 ++it;
12291 }
12292 mData->mSession.mRemoteControls.clear();
12293 }
12294
12295 /*
12296 * An expected uninitialization can come only from #checkForDeath().
12297 * Otherwise it means that something's gone really wrong (for example,
12298 * the Session implementation has released the VirtualBox reference
12299 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12300 * etc). However, it's also possible, that the client releases the IPC
12301 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12302 * but the VirtualBox release event comes first to the server process.
12303 * This case is practically possible, so we should not assert on an
12304 * unexpected uninit, just log a warning.
12305 */
12306
12307 if ((aReason == Uninit::Unexpected))
12308 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12309
12310 if (aReason != Uninit::Normal)
12311 {
12312 mData->mSession.mDirectControl.setNull();
12313 }
12314 else
12315 {
12316 /* this must be null here (see #OnSessionEnd()) */
12317 Assert(mData->mSession.mDirectControl.isNull());
12318 Assert(mData->mSession.mState == SessionState_Unlocking);
12319 Assert(!mData->mSession.mProgress.isNull());
12320 }
12321 if (mData->mSession.mProgress)
12322 {
12323 if (aReason == Uninit::Normal)
12324 mData->mSession.mProgress->notifyComplete(S_OK);
12325 else
12326 mData->mSession.mProgress->notifyComplete(E_FAIL,
12327 COM_IIDOF(ISession),
12328 getComponentName(),
12329 tr("The VM session was aborted"));
12330 mData->mSession.mProgress.setNull();
12331 }
12332
12333 /* remove the association between the peer machine and this session machine */
12334 Assert( (SessionMachine*)mData->mSession.mMachine == this
12335 || aReason == Uninit::Unexpected);
12336
12337 /* reset the rest of session data */
12338 mData->mSession.mMachine.setNull();
12339 mData->mSession.mState = SessionState_Unlocked;
12340 mData->mSession.mType.setNull();
12341
12342 /* close the interprocess semaphore before leaving the exclusive lock */
12343#if defined(RT_OS_WINDOWS)
12344 if (mIPCSem)
12345 ::CloseHandle(mIPCSem);
12346 mIPCSem = NULL;
12347#elif defined(RT_OS_OS2)
12348 if (mIPCSem != NULLHANDLE)
12349 ::DosCloseMutexSem(mIPCSem);
12350 mIPCSem = NULLHANDLE;
12351#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12352 if (mIPCSem >= 0)
12353 ::semctl(mIPCSem, 0, IPC_RMID);
12354 mIPCSem = -1;
12355# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12356 mIPCKey = "0";
12357# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12358#else
12359# error "Port me!"
12360#endif
12361
12362 /* fire an event */
12363 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12364
12365 uninitDataAndChildObjects();
12366
12367 /* free the essential data structure last */
12368 mData.free();
12369
12370 /* release the exclusive lock before setting the below two to NULL */
12371 multilock.release();
12372
12373 unconst(mParent) = NULL;
12374 unconst(mPeer) = NULL;
12375
12376 LogFlowThisFuncLeave();
12377}
12378
12379// util::Lockable interface
12380////////////////////////////////////////////////////////////////////////////////
12381
12382/**
12383 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12384 * with the primary Machine instance (mPeer).
12385 */
12386RWLockHandle *SessionMachine::lockHandle() const
12387{
12388 AssertReturn(mPeer != NULL, NULL);
12389 return mPeer->lockHandle();
12390}
12391
12392// IInternalMachineControl methods
12393////////////////////////////////////////////////////////////////////////////////
12394
12395/**
12396 * Passes collected guest statistics to performance collector object
12397 */
12398STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12399 ULONG aCpuKernel, ULONG aCpuIdle,
12400 ULONG aMemTotal, ULONG aMemFree,
12401 ULONG aMemBalloon, ULONG aMemShared,
12402 ULONG aMemCache, ULONG aPageTotal,
12403 ULONG aAllocVMM, ULONG aFreeVMM,
12404 ULONG aBalloonedVMM, ULONG aSharedVMM,
12405 ULONG aVmNetRx, ULONG aVmNetTx)
12406{
12407 if (mCollectorGuest)
12408 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12409 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12410 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12411 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12412
12413 return S_OK;
12414}
12415
12416/**
12417 * @note Locks this object for writing.
12418 */
12419STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12420{
12421 AutoCaller autoCaller(this);
12422 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12423
12424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12425
12426 mRemoveSavedState = aRemove;
12427
12428 return S_OK;
12429}
12430
12431/**
12432 * @note Locks the same as #setMachineState() does.
12433 */
12434STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12435{
12436 return setMachineState(aMachineState);
12437}
12438
12439/**
12440 * @note Locks this object for reading.
12441 */
12442STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12443{
12444 AutoCaller autoCaller(this);
12445 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12446
12447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12448
12449#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12450 mIPCSemName.cloneTo(aId);
12451 return S_OK;
12452#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12453# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12454 mIPCKey.cloneTo(aId);
12455# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12456 mData->m_strConfigFileFull.cloneTo(aId);
12457# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12458 return S_OK;
12459#else
12460# error "Port me!"
12461#endif
12462}
12463
12464/**
12465 * @note Locks this object for writing.
12466 */
12467STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12468{
12469 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12470 AutoCaller autoCaller(this);
12471 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12472
12473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12474
12475 if (mData->mSession.mState != SessionState_Locked)
12476 return VBOX_E_INVALID_OBJECT_STATE;
12477
12478 if (!mData->mSession.mProgress.isNull())
12479 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12480
12481 LogFlowThisFunc(("returns S_OK.\n"));
12482 return S_OK;
12483}
12484
12485/**
12486 * @note Locks this object for writing.
12487 */
12488STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12489{
12490 AutoCaller autoCaller(this);
12491 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12492
12493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12494
12495 if (mData->mSession.mState != SessionState_Locked)
12496 return VBOX_E_INVALID_OBJECT_STATE;
12497
12498 /* Finalize the LaunchVMProcess progress object. */
12499 if (mData->mSession.mProgress)
12500 {
12501 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12502 mData->mSession.mProgress.setNull();
12503 }
12504
12505 if (SUCCEEDED((HRESULT)iResult))
12506 {
12507#ifdef VBOX_WITH_RESOURCE_USAGE_API
12508 /* The VM has been powered up successfully, so it makes sense
12509 * now to offer the performance metrics for a running machine
12510 * object. Doing it earlier wouldn't be safe. */
12511 registerMetrics(mParent->performanceCollector(), mPeer,
12512 mData->mSession.mPID);
12513#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12514 }
12515
12516 return S_OK;
12517}
12518
12519/**
12520 * @note Locks this object for writing.
12521 */
12522STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12523{
12524 LogFlowThisFuncEnter();
12525
12526 CheckComArgOutPointerValid(aProgress);
12527
12528 AutoCaller autoCaller(this);
12529 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12530
12531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12532
12533 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12534 E_FAIL);
12535
12536 /* create a progress object to track operation completion */
12537 ComObjPtr<Progress> pProgress;
12538 pProgress.createObject();
12539 pProgress->init(getVirtualBox(),
12540 static_cast<IMachine *>(this) /* aInitiator */,
12541 Bstr(tr("Stopping the virtual machine")).raw(),
12542 FALSE /* aCancelable */);
12543
12544 /* fill in the console task data */
12545 mConsoleTaskData.mLastState = mData->mMachineState;
12546 mConsoleTaskData.mProgress = pProgress;
12547
12548 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12549 setMachineState(MachineState_Stopping);
12550
12551 pProgress.queryInterfaceTo(aProgress);
12552
12553 return S_OK;
12554}
12555
12556/**
12557 * @note Locks this object for writing.
12558 */
12559STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12560{
12561 LogFlowThisFuncEnter();
12562
12563 AutoCaller autoCaller(this);
12564 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12565
12566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12567
12568 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12569 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12570 && mConsoleTaskData.mLastState != MachineState_Null,
12571 E_FAIL);
12572
12573 /*
12574 * On failure, set the state to the state we had when BeginPoweringDown()
12575 * was called (this is expected by Console::PowerDown() and the associated
12576 * task). On success the VM process already changed the state to
12577 * MachineState_PoweredOff, so no need to do anything.
12578 */
12579 if (FAILED(iResult))
12580 setMachineState(mConsoleTaskData.mLastState);
12581
12582 /* notify the progress object about operation completion */
12583 Assert(mConsoleTaskData.mProgress);
12584 if (SUCCEEDED(iResult))
12585 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12586 else
12587 {
12588 Utf8Str strErrMsg(aErrMsg);
12589 if (strErrMsg.length())
12590 mConsoleTaskData.mProgress->notifyComplete(iResult,
12591 COM_IIDOF(ISession),
12592 getComponentName(),
12593 strErrMsg.c_str());
12594 else
12595 mConsoleTaskData.mProgress->notifyComplete(iResult);
12596 }
12597
12598 /* clear out the temporary saved state data */
12599 mConsoleTaskData.mLastState = MachineState_Null;
12600 mConsoleTaskData.mProgress.setNull();
12601
12602 LogFlowThisFuncLeave();
12603 return S_OK;
12604}
12605
12606
12607/**
12608 * Goes through the USB filters of the given machine to see if the given
12609 * device matches any filter or not.
12610 *
12611 * @note Locks the same as USBController::hasMatchingFilter() does.
12612 */
12613STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12614 BOOL *aMatched,
12615 ULONG *aMaskedIfs)
12616{
12617 LogFlowThisFunc(("\n"));
12618
12619 CheckComArgNotNull(aUSBDevice);
12620 CheckComArgOutPointerValid(aMatched);
12621
12622 AutoCaller autoCaller(this);
12623 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12624
12625#ifdef VBOX_WITH_USB
12626 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12627#else
12628 NOREF(aUSBDevice);
12629 NOREF(aMaskedIfs);
12630 *aMatched = FALSE;
12631#endif
12632
12633 return S_OK;
12634}
12635
12636/**
12637 * @note Locks the same as Host::captureUSBDevice() does.
12638 */
12639STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12640{
12641 LogFlowThisFunc(("\n"));
12642
12643 AutoCaller autoCaller(this);
12644 AssertComRCReturnRC(autoCaller.rc());
12645
12646#ifdef VBOX_WITH_USB
12647 /* if captureDeviceForVM() fails, it must have set extended error info */
12648 clearError();
12649 MultiResult rc = mParent->host()->checkUSBProxyService();
12650 if (FAILED(rc)) return rc;
12651
12652 USBProxyService *service = mParent->host()->usbProxyService();
12653 AssertReturn(service, E_FAIL);
12654 return service->captureDeviceForVM(this, Guid(aId).ref());
12655#else
12656 NOREF(aId);
12657 return E_NOTIMPL;
12658#endif
12659}
12660
12661/**
12662 * @note Locks the same as Host::detachUSBDevice() does.
12663 */
12664STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12665{
12666 LogFlowThisFunc(("\n"));
12667
12668 AutoCaller autoCaller(this);
12669 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12670
12671#ifdef VBOX_WITH_USB
12672 USBProxyService *service = mParent->host()->usbProxyService();
12673 AssertReturn(service, E_FAIL);
12674 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12675#else
12676 NOREF(aId);
12677 NOREF(aDone);
12678 return E_NOTIMPL;
12679#endif
12680}
12681
12682/**
12683 * Inserts all machine filters to the USB proxy service and then calls
12684 * Host::autoCaptureUSBDevices().
12685 *
12686 * Called by Console from the VM process upon VM startup.
12687 *
12688 * @note Locks what called methods lock.
12689 */
12690STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12691{
12692 LogFlowThisFunc(("\n"));
12693
12694 AutoCaller autoCaller(this);
12695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12696
12697#ifdef VBOX_WITH_USB
12698 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12699 AssertComRC(rc);
12700 NOREF(rc);
12701
12702 USBProxyService *service = mParent->host()->usbProxyService();
12703 AssertReturn(service, E_FAIL);
12704 return service->autoCaptureDevicesForVM(this);
12705#else
12706 return S_OK;
12707#endif
12708}
12709
12710/**
12711 * Removes all machine filters from the USB proxy service and then calls
12712 * Host::detachAllUSBDevices().
12713 *
12714 * Called by Console from the VM process upon normal VM termination or by
12715 * SessionMachine::uninit() upon abnormal VM termination (from under the
12716 * Machine/SessionMachine lock).
12717 *
12718 * @note Locks what called methods lock.
12719 */
12720STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12721{
12722 LogFlowThisFunc(("\n"));
12723
12724 AutoCaller autoCaller(this);
12725 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12726
12727#ifdef VBOX_WITH_USB
12728 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12729 AssertComRC(rc);
12730 NOREF(rc);
12731
12732 USBProxyService *service = mParent->host()->usbProxyService();
12733 AssertReturn(service, E_FAIL);
12734 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12735#else
12736 NOREF(aDone);
12737 return S_OK;
12738#endif
12739}
12740
12741/**
12742 * @note Locks this object for writing.
12743 */
12744STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12745 IProgress **aProgress)
12746{
12747 LogFlowThisFuncEnter();
12748
12749 AssertReturn(aSession, E_INVALIDARG);
12750 AssertReturn(aProgress, E_INVALIDARG);
12751
12752 AutoCaller autoCaller(this);
12753
12754 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12755 /*
12756 * We don't assert below because it might happen that a non-direct session
12757 * informs us it is closed right after we've been uninitialized -- it's ok.
12758 */
12759 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12760
12761 /* get IInternalSessionControl interface */
12762 ComPtr<IInternalSessionControl> control(aSession);
12763
12764 ComAssertRet(!control.isNull(), E_INVALIDARG);
12765
12766 /* Creating a Progress object requires the VirtualBox lock, and
12767 * thus locking it here is required by the lock order rules. */
12768 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12769
12770 if (control == mData->mSession.mDirectControl)
12771 {
12772 ComAssertRet(aProgress, E_POINTER);
12773
12774 /* The direct session is being normally closed by the client process
12775 * ----------------------------------------------------------------- */
12776
12777 /* go to the closing state (essential for all open*Session() calls and
12778 * for #checkForDeath()) */
12779 Assert(mData->mSession.mState == SessionState_Locked);
12780 mData->mSession.mState = SessionState_Unlocking;
12781
12782 /* set direct control to NULL to release the remote instance */
12783 mData->mSession.mDirectControl.setNull();
12784 LogFlowThisFunc(("Direct control is set to NULL\n"));
12785
12786 if (mData->mSession.mProgress)
12787 {
12788 /* finalize the progress, someone might wait if a frontend
12789 * closes the session before powering on the VM. */
12790 mData->mSession.mProgress->notifyComplete(E_FAIL,
12791 COM_IIDOF(ISession),
12792 getComponentName(),
12793 tr("The VM session was closed before any attempt to power it on"));
12794 mData->mSession.mProgress.setNull();
12795 }
12796
12797 /* Create the progress object the client will use to wait until
12798 * #checkForDeath() is called to uninitialize this session object after
12799 * it releases the IPC semaphore.
12800 * Note! Because we're "reusing" mProgress here, this must be a proxy
12801 * object just like for LaunchVMProcess. */
12802 Assert(mData->mSession.mProgress.isNull());
12803 ComObjPtr<ProgressProxy> progress;
12804 progress.createObject();
12805 ComPtr<IUnknown> pPeer(mPeer);
12806 progress->init(mParent, pPeer,
12807 Bstr(tr("Closing session")).raw(),
12808 FALSE /* aCancelable */);
12809 progress.queryInterfaceTo(aProgress);
12810 mData->mSession.mProgress = progress;
12811 }
12812 else
12813 {
12814 /* the remote session is being normally closed */
12815 Data::Session::RemoteControlList::iterator it =
12816 mData->mSession.mRemoteControls.begin();
12817 while (it != mData->mSession.mRemoteControls.end())
12818 {
12819 if (control == *it)
12820 break;
12821 ++it;
12822 }
12823 BOOL found = it != mData->mSession.mRemoteControls.end();
12824 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12825 E_INVALIDARG);
12826 // This MUST be erase(it), not remove(*it) as the latter triggers a
12827 // very nasty use after free due to the place where the value "lives".
12828 mData->mSession.mRemoteControls.erase(it);
12829 }
12830
12831 /* signal the client watcher thread, because the client is going away */
12832 mParent->updateClientWatcher();
12833
12834 LogFlowThisFuncLeave();
12835 return S_OK;
12836}
12837
12838/**
12839 * @note Locks this object for writing.
12840 */
12841STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12842{
12843 LogFlowThisFuncEnter();
12844
12845 CheckComArgOutPointerValid(aProgress);
12846 CheckComArgOutPointerValid(aStateFilePath);
12847
12848 AutoCaller autoCaller(this);
12849 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12850
12851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12852
12853 AssertReturn( mData->mMachineState == MachineState_Paused
12854 && mConsoleTaskData.mLastState == MachineState_Null
12855 && mConsoleTaskData.strStateFilePath.isEmpty(),
12856 E_FAIL);
12857
12858 /* create a progress object to track operation completion */
12859 ComObjPtr<Progress> pProgress;
12860 pProgress.createObject();
12861 pProgress->init(getVirtualBox(),
12862 static_cast<IMachine *>(this) /* aInitiator */,
12863 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12864 FALSE /* aCancelable */);
12865
12866 Utf8Str strStateFilePath;
12867 /* stateFilePath is null when the machine is not running */
12868 if (mData->mMachineState == MachineState_Paused)
12869 composeSavedStateFilename(strStateFilePath);
12870
12871 /* fill in the console task data */
12872 mConsoleTaskData.mLastState = mData->mMachineState;
12873 mConsoleTaskData.strStateFilePath = strStateFilePath;
12874 mConsoleTaskData.mProgress = pProgress;
12875
12876 /* set the state to Saving (this is expected by Console::SaveState()) */
12877 setMachineState(MachineState_Saving);
12878
12879 strStateFilePath.cloneTo(aStateFilePath);
12880 pProgress.queryInterfaceTo(aProgress);
12881
12882 return S_OK;
12883}
12884
12885/**
12886 * @note Locks mParent + this object for writing.
12887 */
12888STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12889{
12890 LogFlowThisFunc(("\n"));
12891
12892 AutoCaller autoCaller(this);
12893 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12894
12895 /* endSavingState() need mParent lock */
12896 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12897
12898 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12899 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12900 && mConsoleTaskData.mLastState != MachineState_Null
12901 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12902 E_FAIL);
12903
12904 /*
12905 * On failure, set the state to the state we had when BeginSavingState()
12906 * was called (this is expected by Console::SaveState() and the associated
12907 * task). On success the VM process already changed the state to
12908 * MachineState_Saved, so no need to do anything.
12909 */
12910 if (FAILED(iResult))
12911 setMachineState(mConsoleTaskData.mLastState);
12912
12913 return endSavingState(iResult, aErrMsg);
12914}
12915
12916/**
12917 * @note Locks this object for writing.
12918 */
12919STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12920{
12921 LogFlowThisFunc(("\n"));
12922
12923 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12924
12925 AutoCaller autoCaller(this);
12926 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12927
12928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12929
12930 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12931 || mData->mMachineState == MachineState_Teleported
12932 || mData->mMachineState == MachineState_Aborted
12933 , E_FAIL); /** @todo setError. */
12934
12935 Utf8Str stateFilePathFull = aSavedStateFile;
12936 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12937 if (RT_FAILURE(vrc))
12938 return setError(VBOX_E_FILE_ERROR,
12939 tr("Invalid saved state file path '%ls' (%Rrc)"),
12940 aSavedStateFile,
12941 vrc);
12942
12943 mSSData->strStateFilePath = stateFilePathFull;
12944
12945 /* The below setMachineState() will detect the state transition and will
12946 * update the settings file */
12947
12948 return setMachineState(MachineState_Saved);
12949}
12950
12951STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
12952 ComSafeArrayOut(BSTR, aValues),
12953 ComSafeArrayOut(LONG64, aTimestamps),
12954 ComSafeArrayOut(BSTR, aFlags))
12955{
12956 LogFlowThisFunc(("\n"));
12957
12958#ifdef VBOX_WITH_GUEST_PROPS
12959 using namespace guestProp;
12960
12961 AutoCaller autoCaller(this);
12962 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12963
12964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12965
12966 CheckComArgOutSafeArrayPointerValid(aNames);
12967 CheckComArgOutSafeArrayPointerValid(aValues);
12968 CheckComArgOutSafeArrayPointerValid(aTimestamps);
12969 CheckComArgOutSafeArrayPointerValid(aFlags);
12970
12971 size_t cEntries = mHWData->mGuestProperties.size();
12972 com::SafeArray<BSTR> names(cEntries);
12973 com::SafeArray<BSTR> values(cEntries);
12974 com::SafeArray<LONG64> timestamps(cEntries);
12975 com::SafeArray<BSTR> flags(cEntries);
12976 unsigned i = 0;
12977 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
12978 it != mHWData->mGuestProperties.end();
12979 ++it)
12980 {
12981 char szFlags[MAX_FLAGS_LEN + 1];
12982 it->first.cloneTo(&names[i]);
12983 it->second.strValue.cloneTo(&values[i]);
12984 timestamps[i] = it->second.mTimestamp;
12985 /* If it is NULL, keep it NULL. */
12986 if (it->second.mFlags)
12987 {
12988 writeFlags(it->second.mFlags, szFlags);
12989 Bstr(szFlags).cloneTo(&flags[i]);
12990 }
12991 else
12992 flags[i] = NULL;
12993 ++i;
12994 }
12995 names.detachTo(ComSafeArrayOutArg(aNames));
12996 values.detachTo(ComSafeArrayOutArg(aValues));
12997 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
12998 flags.detachTo(ComSafeArrayOutArg(aFlags));
12999 return S_OK;
13000#else
13001 ReturnComNotImplemented();
13002#endif
13003}
13004
13005STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13006 IN_BSTR aValue,
13007 LONG64 aTimestamp,
13008 IN_BSTR aFlags)
13009{
13010 LogFlowThisFunc(("\n"));
13011
13012#ifdef VBOX_WITH_GUEST_PROPS
13013 using namespace guestProp;
13014
13015 CheckComArgStrNotEmptyOrNull(aName);
13016 CheckComArgNotNull(aValue);
13017 CheckComArgNotNull(aFlags);
13018
13019 try
13020 {
13021 /*
13022 * Convert input up front.
13023 */
13024 Utf8Str utf8Name(aName);
13025 uint32_t fFlags = NILFLAG;
13026 if (aFlags)
13027 {
13028 Utf8Str utf8Flags(aFlags);
13029 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13030 AssertRCReturn(vrc, E_INVALIDARG);
13031 }
13032
13033 /*
13034 * Now grab the object lock, validate the state and do the update.
13035 */
13036 AutoCaller autoCaller(this);
13037 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13038
13039 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13040
13041 switch (mData->mMachineState)
13042 {
13043 case MachineState_Paused:
13044 case MachineState_Running:
13045 case MachineState_Teleporting:
13046 case MachineState_TeleportingPausedVM:
13047 case MachineState_LiveSnapshotting:
13048 case MachineState_DeletingSnapshotOnline:
13049 case MachineState_DeletingSnapshotPaused:
13050 case MachineState_Saving:
13051 case MachineState_Stopping:
13052 break;
13053
13054 default:
13055 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13056 VBOX_E_INVALID_VM_STATE);
13057 }
13058
13059 setModified(IsModified_MachineData);
13060 mHWData.backup();
13061
13062 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13063 if (it != mHWData->mGuestProperties.end())
13064 {
13065 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13066 {
13067 it->second.strValue = aValue;
13068 it->second.mFlags = fFlags;
13069 it->second.mTimestamp = aTimestamp;
13070 }
13071 else
13072 mHWData->mGuestProperties.erase(it);
13073
13074 mData->mGuestPropertiesModified = TRUE;
13075 }
13076
13077 /*
13078 * Send a callback notification if appropriate
13079 */
13080 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13081 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13082 RTSTR_MAX,
13083 utf8Name.c_str(),
13084 RTSTR_MAX, NULL)
13085 )
13086 {
13087 alock.release();
13088
13089 mParent->onGuestPropertyChange(mData->mUuid,
13090 aName,
13091 aValue,
13092 aFlags);
13093 }
13094 }
13095 catch (...)
13096 {
13097 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13098 }
13099 return S_OK;
13100#else
13101 ReturnComNotImplemented();
13102#endif
13103}
13104
13105STDMETHODIMP SessionMachine::LockMedia()
13106{
13107 AutoCaller autoCaller(this);
13108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13109
13110 AutoMultiWriteLock2 alock(this->lockHandle(),
13111 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13112
13113 AssertReturn( mData->mMachineState == MachineState_Starting
13114 || mData->mMachineState == MachineState_Restoring
13115 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13116
13117 clearError();
13118 alock.release();
13119 return lockMedia();
13120}
13121
13122STDMETHODIMP SessionMachine::UnlockMedia()
13123{
13124 unlockMedia();
13125 return S_OK;
13126}
13127
13128STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13129 IMediumAttachment **aNewAttachment)
13130{
13131 CheckComArgNotNull(aAttachment);
13132 CheckComArgOutPointerValid(aNewAttachment);
13133
13134 AutoCaller autoCaller(this);
13135 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13136
13137 // request the host lock first, since might be calling Host methods for getting host drives;
13138 // next, protect the media tree all the while we're in here, as well as our member variables
13139 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13140 this->lockHandle(),
13141 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13142
13143 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13144
13145 Bstr ctrlName;
13146 LONG lPort;
13147 LONG lDevice;
13148 bool fTempEject;
13149 {
13150 AutoCaller autoAttachCaller(this);
13151 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13152
13153 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13154
13155 /* Need to query the details first, as the IMediumAttachment reference
13156 * might be to the original settings, which we are going to change. */
13157 ctrlName = pAttach->getControllerName();
13158 lPort = pAttach->getPort();
13159 lDevice = pAttach->getDevice();
13160 fTempEject = pAttach->getTempEject();
13161 }
13162
13163 if (!fTempEject)
13164 {
13165 /* Remember previously mounted medium. The medium before taking the
13166 * backup is not necessarily the same thing. */
13167 ComObjPtr<Medium> oldmedium;
13168 oldmedium = pAttach->getMedium();
13169
13170 setModified(IsModified_Storage);
13171 mMediaData.backup();
13172
13173 // The backup operation makes the pAttach reference point to the
13174 // old settings. Re-get the correct reference.
13175 pAttach = findAttachment(mMediaData->mAttachments,
13176 ctrlName.raw(),
13177 lPort,
13178 lDevice);
13179
13180 {
13181 AutoCaller autoAttachCaller(this);
13182 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13183
13184 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13185 if (!oldmedium.isNull())
13186 oldmedium->removeBackReference(mData->mUuid);
13187
13188 pAttach->updateMedium(NULL);
13189 pAttach->updateEjected();
13190 }
13191
13192 setModified(IsModified_Storage);
13193 }
13194 else
13195 {
13196 {
13197 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13198 pAttach->updateEjected();
13199 }
13200 }
13201
13202 pAttach.queryInterfaceTo(aNewAttachment);
13203
13204 return S_OK;
13205}
13206
13207// public methods only for internal purposes
13208/////////////////////////////////////////////////////////////////////////////
13209
13210/**
13211 * Called from the client watcher thread to check for expected or unexpected
13212 * death of the client process that has a direct session to this machine.
13213 *
13214 * On Win32 and on OS/2, this method is called only when we've got the
13215 * mutex (i.e. the client has either died or terminated normally) so it always
13216 * returns @c true (the client is terminated, the session machine is
13217 * uninitialized).
13218 *
13219 * On other platforms, the method returns @c true if the client process has
13220 * terminated normally or abnormally and the session machine was uninitialized,
13221 * and @c false if the client process is still alive.
13222 *
13223 * @note Locks this object for writing.
13224 */
13225bool SessionMachine::checkForDeath()
13226{
13227 Uninit::Reason reason;
13228 bool terminated = false;
13229
13230 /* Enclose autoCaller with a block because calling uninit() from under it
13231 * will deadlock. */
13232 {
13233 AutoCaller autoCaller(this);
13234 if (!autoCaller.isOk())
13235 {
13236 /* return true if not ready, to cause the client watcher to exclude
13237 * the corresponding session from watching */
13238 LogFlowThisFunc(("Already uninitialized!\n"));
13239 return true;
13240 }
13241
13242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13243
13244 /* Determine the reason of death: if the session state is Closing here,
13245 * everything is fine. Otherwise it means that the client did not call
13246 * OnSessionEnd() before it released the IPC semaphore. This may happen
13247 * either because the client process has abnormally terminated, or
13248 * because it simply forgot to call ISession::Close() before exiting. We
13249 * threat the latter also as an abnormal termination (see
13250 * Session::uninit() for details). */
13251 reason = mData->mSession.mState == SessionState_Unlocking ?
13252 Uninit::Normal :
13253 Uninit::Abnormal;
13254
13255#if defined(RT_OS_WINDOWS)
13256
13257 AssertMsg(mIPCSem, ("semaphore must be created"));
13258
13259 /* release the IPC mutex */
13260 ::ReleaseMutex(mIPCSem);
13261
13262 terminated = true;
13263
13264#elif defined(RT_OS_OS2)
13265
13266 AssertMsg(mIPCSem, ("semaphore must be created"));
13267
13268 /* release the IPC mutex */
13269 ::DosReleaseMutexSem(mIPCSem);
13270
13271 terminated = true;
13272
13273#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13274
13275 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13276
13277 int val = ::semctl(mIPCSem, 0, GETVAL);
13278 if (val > 0)
13279 {
13280 /* the semaphore is signaled, meaning the session is terminated */
13281 terminated = true;
13282 }
13283
13284#else
13285# error "Port me!"
13286#endif
13287
13288 } /* AutoCaller block */
13289
13290 if (terminated)
13291 uninit(reason);
13292
13293 return terminated;
13294}
13295
13296/**
13297 * @note Locks this object for reading.
13298 */
13299HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13300{
13301 LogFlowThisFunc(("\n"));
13302
13303 AutoCaller autoCaller(this);
13304 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13305
13306 ComPtr<IInternalSessionControl> directControl;
13307 {
13308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13309 directControl = mData->mSession.mDirectControl;
13310 }
13311
13312 /* ignore notifications sent after #OnSessionEnd() is called */
13313 if (!directControl)
13314 return S_OK;
13315
13316 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13317}
13318
13319/**
13320 * @note Locks this object for reading.
13321 */
13322HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13323 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13324{
13325 LogFlowThisFunc(("\n"));
13326
13327 AutoCaller autoCaller(this);
13328 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13329
13330 ComPtr<IInternalSessionControl> directControl;
13331 {
13332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13333 directControl = mData->mSession.mDirectControl;
13334 }
13335
13336 /* ignore notifications sent after #OnSessionEnd() is called */
13337 if (!directControl)
13338 return S_OK;
13339 /*
13340 * instead acting like callback we ask IVirtualBox deliver corresponding event
13341 */
13342
13343 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13344 return S_OK;
13345}
13346
13347/**
13348 * @note Locks this object for reading.
13349 */
13350HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13351{
13352 LogFlowThisFunc(("\n"));
13353
13354 AutoCaller autoCaller(this);
13355 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13356
13357 ComPtr<IInternalSessionControl> directControl;
13358 {
13359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13360 directControl = mData->mSession.mDirectControl;
13361 }
13362
13363 /* ignore notifications sent after #OnSessionEnd() is called */
13364 if (!directControl)
13365 return S_OK;
13366
13367 return directControl->OnSerialPortChange(serialPort);
13368}
13369
13370/**
13371 * @note Locks this object for reading.
13372 */
13373HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13374{
13375 LogFlowThisFunc(("\n"));
13376
13377 AutoCaller autoCaller(this);
13378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13379
13380 ComPtr<IInternalSessionControl> directControl;
13381 {
13382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13383 directControl = mData->mSession.mDirectControl;
13384 }
13385
13386 /* ignore notifications sent after #OnSessionEnd() is called */
13387 if (!directControl)
13388 return S_OK;
13389
13390 return directControl->OnParallelPortChange(parallelPort);
13391}
13392
13393/**
13394 * @note Locks this object for reading.
13395 */
13396HRESULT SessionMachine::onStorageControllerChange()
13397{
13398 LogFlowThisFunc(("\n"));
13399
13400 AutoCaller autoCaller(this);
13401 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13402
13403 ComPtr<IInternalSessionControl> directControl;
13404 {
13405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13406 directControl = mData->mSession.mDirectControl;
13407 }
13408
13409 /* ignore notifications sent after #OnSessionEnd() is called */
13410 if (!directControl)
13411 return S_OK;
13412
13413 return directControl->OnStorageControllerChange();
13414}
13415
13416/**
13417 * @note Locks this object for reading.
13418 */
13419HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13420{
13421 LogFlowThisFunc(("\n"));
13422
13423 AutoCaller autoCaller(this);
13424 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13425
13426 ComPtr<IInternalSessionControl> directControl;
13427 {
13428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13429 directControl = mData->mSession.mDirectControl;
13430 }
13431
13432 /* ignore notifications sent after #OnSessionEnd() is called */
13433 if (!directControl)
13434 return S_OK;
13435
13436 return directControl->OnMediumChange(aAttachment, aForce);
13437}
13438
13439/**
13440 * @note Locks this object for reading.
13441 */
13442HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13443{
13444 LogFlowThisFunc(("\n"));
13445
13446 AutoCaller autoCaller(this);
13447 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13448
13449 ComPtr<IInternalSessionControl> directControl;
13450 {
13451 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13452 directControl = mData->mSession.mDirectControl;
13453 }
13454
13455 /* ignore notifications sent after #OnSessionEnd() is called */
13456 if (!directControl)
13457 return S_OK;
13458
13459 return directControl->OnCPUChange(aCPU, aRemove);
13460}
13461
13462HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13463{
13464 LogFlowThisFunc(("\n"));
13465
13466 AutoCaller autoCaller(this);
13467 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13468
13469 ComPtr<IInternalSessionControl> directControl;
13470 {
13471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13472 directControl = mData->mSession.mDirectControl;
13473 }
13474
13475 /* ignore notifications sent after #OnSessionEnd() is called */
13476 if (!directControl)
13477 return S_OK;
13478
13479 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13480}
13481
13482/**
13483 * @note Locks this object for reading.
13484 */
13485HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13486{
13487 LogFlowThisFunc(("\n"));
13488
13489 AutoCaller autoCaller(this);
13490 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13491
13492 ComPtr<IInternalSessionControl> directControl;
13493 {
13494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13495 directControl = mData->mSession.mDirectControl;
13496 }
13497
13498 /* ignore notifications sent after #OnSessionEnd() is called */
13499 if (!directControl)
13500 return S_OK;
13501
13502 return directControl->OnVRDEServerChange(aRestart);
13503}
13504
13505/**
13506 * @note Locks this object for reading.
13507 */
13508HRESULT SessionMachine::onUSBControllerChange()
13509{
13510 LogFlowThisFunc(("\n"));
13511
13512 AutoCaller autoCaller(this);
13513 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13514
13515 ComPtr<IInternalSessionControl> directControl;
13516 {
13517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13518 directControl = mData->mSession.mDirectControl;
13519 }
13520
13521 /* ignore notifications sent after #OnSessionEnd() is called */
13522 if (!directControl)
13523 return S_OK;
13524
13525 return directControl->OnUSBControllerChange();
13526}
13527
13528/**
13529 * @note Locks this object for reading.
13530 */
13531HRESULT SessionMachine::onSharedFolderChange()
13532{
13533 LogFlowThisFunc(("\n"));
13534
13535 AutoCaller autoCaller(this);
13536 AssertComRCReturnRC(autoCaller.rc());
13537
13538 ComPtr<IInternalSessionControl> directControl;
13539 {
13540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13541 directControl = mData->mSession.mDirectControl;
13542 }
13543
13544 /* ignore notifications sent after #OnSessionEnd() is called */
13545 if (!directControl)
13546 return S_OK;
13547
13548 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13549}
13550
13551/**
13552 * @note Locks this object for reading.
13553 */
13554HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13555{
13556 LogFlowThisFunc(("\n"));
13557
13558 AutoCaller autoCaller(this);
13559 AssertComRCReturnRC(autoCaller.rc());
13560
13561 ComPtr<IInternalSessionControl> directControl;
13562 {
13563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13564 directControl = mData->mSession.mDirectControl;
13565 }
13566
13567 /* ignore notifications sent after #OnSessionEnd() is called */
13568 if (!directControl)
13569 return S_OK;
13570
13571 return directControl->OnClipboardModeChange(aClipboardMode);
13572}
13573
13574/**
13575 * @note Locks this object for reading.
13576 */
13577HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13578{
13579 LogFlowThisFunc(("\n"));
13580
13581 AutoCaller autoCaller(this);
13582 AssertComRCReturnRC(autoCaller.rc());
13583
13584 ComPtr<IInternalSessionControl> directControl;
13585 {
13586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13587 directControl = mData->mSession.mDirectControl;
13588 }
13589
13590 /* ignore notifications sent after #OnSessionEnd() is called */
13591 if (!directControl)
13592 return S_OK;
13593
13594 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13595}
13596
13597/**
13598 * @note Locks this object for reading.
13599 */
13600HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13601{
13602 LogFlowThisFunc(("\n"));
13603
13604 AutoCaller autoCaller(this);
13605 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13606
13607 ComPtr<IInternalSessionControl> directControl;
13608 {
13609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13610 directControl = mData->mSession.mDirectControl;
13611 }
13612
13613 /* ignore notifications sent after #OnSessionEnd() is called */
13614 if (!directControl)
13615 return S_OK;
13616
13617 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13618}
13619
13620/**
13621 * @note Locks this object for reading.
13622 */
13623HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13624{
13625 LogFlowThisFunc(("\n"));
13626
13627 AutoCaller autoCaller(this);
13628 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13629
13630 ComPtr<IInternalSessionControl> directControl;
13631 {
13632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13633 directControl = mData->mSession.mDirectControl;
13634 }
13635
13636 /* ignore notifications sent after #OnSessionEnd() is called */
13637 if (!directControl)
13638 return S_OK;
13639
13640 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13641}
13642
13643/**
13644 * Returns @c true if this machine's USB controller reports it has a matching
13645 * filter for the given USB device and @c false otherwise.
13646 *
13647 * @note locks this object for reading.
13648 */
13649bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13650{
13651 AutoCaller autoCaller(this);
13652 /* silently return if not ready -- this method may be called after the
13653 * direct machine session has been called */
13654 if (!autoCaller.isOk())
13655 return false;
13656
13657#ifdef VBOX_WITH_USB
13658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13659
13660 switch (mData->mMachineState)
13661 {
13662 case MachineState_Starting:
13663 case MachineState_Restoring:
13664 case MachineState_TeleportingIn:
13665 case MachineState_Paused:
13666 case MachineState_Running:
13667 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13668 * elsewhere... */
13669 alock.release();
13670 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13671 default: break;
13672 }
13673#else
13674 NOREF(aDevice);
13675 NOREF(aMaskedIfs);
13676#endif
13677 return false;
13678}
13679
13680/**
13681 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13682 */
13683HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13684 IVirtualBoxErrorInfo *aError,
13685 ULONG aMaskedIfs)
13686{
13687 LogFlowThisFunc(("\n"));
13688
13689 AutoCaller autoCaller(this);
13690
13691 /* This notification may happen after the machine object has been
13692 * uninitialized (the session was closed), so don't assert. */
13693 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13694
13695 ComPtr<IInternalSessionControl> directControl;
13696 {
13697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13698 directControl = mData->mSession.mDirectControl;
13699 }
13700
13701 /* fail on notifications sent after #OnSessionEnd() is called, it is
13702 * expected by the caller */
13703 if (!directControl)
13704 return E_FAIL;
13705
13706 /* No locks should be held at this point. */
13707 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13708 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13709
13710 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13711}
13712
13713/**
13714 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13715 */
13716HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13717 IVirtualBoxErrorInfo *aError)
13718{
13719 LogFlowThisFunc(("\n"));
13720
13721 AutoCaller autoCaller(this);
13722
13723 /* This notification may happen after the machine object has been
13724 * uninitialized (the session was closed), so don't assert. */
13725 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13726
13727 ComPtr<IInternalSessionControl> directControl;
13728 {
13729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13730 directControl = mData->mSession.mDirectControl;
13731 }
13732
13733 /* fail on notifications sent after #OnSessionEnd() is called, it is
13734 * expected by the caller */
13735 if (!directControl)
13736 return E_FAIL;
13737
13738 /* No locks should be held at this point. */
13739 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13740 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13741
13742 return directControl->OnUSBDeviceDetach(aId, aError);
13743}
13744
13745// protected methods
13746/////////////////////////////////////////////////////////////////////////////
13747
13748/**
13749 * Helper method to finalize saving the state.
13750 *
13751 * @note Must be called from under this object's lock.
13752 *
13753 * @param aRc S_OK if the snapshot has been taken successfully
13754 * @param aErrMsg human readable error message for failure
13755 *
13756 * @note Locks mParent + this objects for writing.
13757 */
13758HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13759{
13760 LogFlowThisFuncEnter();
13761
13762 AutoCaller autoCaller(this);
13763 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13764
13765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13766
13767 HRESULT rc = S_OK;
13768
13769 if (SUCCEEDED(aRc))
13770 {
13771 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13772
13773 /* save all VM settings */
13774 rc = saveSettings(NULL);
13775 // no need to check whether VirtualBox.xml needs saving also since
13776 // we can't have a name change pending at this point
13777 }
13778 else
13779 {
13780 // delete the saved state file (it might have been already created);
13781 // we need not check whether this is shared with a snapshot here because
13782 // we certainly created this saved state file here anew
13783 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13784 }
13785
13786 /* notify the progress object about operation completion */
13787 Assert(mConsoleTaskData.mProgress);
13788 if (SUCCEEDED(aRc))
13789 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13790 else
13791 {
13792 if (aErrMsg.length())
13793 mConsoleTaskData.mProgress->notifyComplete(aRc,
13794 COM_IIDOF(ISession),
13795 getComponentName(),
13796 aErrMsg.c_str());
13797 else
13798 mConsoleTaskData.mProgress->notifyComplete(aRc);
13799 }
13800
13801 /* clear out the temporary saved state data */
13802 mConsoleTaskData.mLastState = MachineState_Null;
13803 mConsoleTaskData.strStateFilePath.setNull();
13804 mConsoleTaskData.mProgress.setNull();
13805
13806 LogFlowThisFuncLeave();
13807 return rc;
13808}
13809
13810/**
13811 * Deletes the given file if it is no longer in use by either the current machine state
13812 * (if the machine is "saved") or any of the machine's snapshots.
13813 *
13814 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13815 * but is different for each SnapshotMachine. When calling this, the order of calling this
13816 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13817 * is therefore critical. I know, it's all rather messy.
13818 *
13819 * @param strStateFile
13820 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13821 */
13822void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13823 Snapshot *pSnapshotToIgnore)
13824{
13825 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13826 if ( (strStateFile.isNotEmpty())
13827 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13828 )
13829 // ... and it must also not be shared with other snapshots
13830 if ( !mData->mFirstSnapshot
13831 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13832 // this checks the SnapshotMachine's state file paths
13833 )
13834 RTFileDelete(strStateFile.c_str());
13835}
13836
13837/**
13838 * Locks the attached media.
13839 *
13840 * All attached hard disks are locked for writing and DVD/floppy are locked for
13841 * reading. Parents of attached hard disks (if any) are locked for reading.
13842 *
13843 * This method also performs accessibility check of all media it locks: if some
13844 * media is inaccessible, the method will return a failure and a bunch of
13845 * extended error info objects per each inaccessible medium.
13846 *
13847 * Note that this method is atomic: if it returns a success, all media are
13848 * locked as described above; on failure no media is locked at all (all
13849 * succeeded individual locks will be undone).
13850 *
13851 * The caller is responsible for doing the necessary state sanity checks.
13852 *
13853 * The locks made by this method must be undone by calling #unlockMedia() when
13854 * no more needed.
13855 */
13856HRESULT SessionMachine::lockMedia()
13857{
13858 AutoCaller autoCaller(this);
13859 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13860
13861 AutoMultiWriteLock2 alock(this->lockHandle(),
13862 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13863
13864 /* bail out if trying to lock things with already set up locking */
13865 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13866
13867 MultiResult mrc(S_OK);
13868
13869 /* Collect locking information for all medium objects attached to the VM. */
13870 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13871 it != mMediaData->mAttachments.end();
13872 ++it)
13873 {
13874 MediumAttachment* pAtt = *it;
13875 DeviceType_T devType = pAtt->getType();
13876 Medium *pMedium = pAtt->getMedium();
13877
13878 MediumLockList *pMediumLockList(new MediumLockList());
13879 // There can be attachments without a medium (floppy/dvd), and thus
13880 // it's impossible to create a medium lock list. It still makes sense
13881 // to have the empty medium lock list in the map in case a medium is
13882 // attached later.
13883 if (pMedium != NULL)
13884 {
13885 MediumType_T mediumType = pMedium->getType();
13886 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13887 || mediumType == MediumType_Shareable;
13888 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13889
13890 alock.release();
13891 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13892 !fIsReadOnlyLock /* fMediumLockWrite */,
13893 NULL,
13894 *pMediumLockList);
13895 alock.acquire();
13896 if (FAILED(mrc))
13897 {
13898 delete pMediumLockList;
13899 mData->mSession.mLockedMedia.Clear();
13900 break;
13901 }
13902 }
13903
13904 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13905 if (FAILED(rc))
13906 {
13907 mData->mSession.mLockedMedia.Clear();
13908 mrc = setError(rc,
13909 tr("Collecting locking information for all attached media failed"));
13910 break;
13911 }
13912 }
13913
13914 if (SUCCEEDED(mrc))
13915 {
13916 /* Now lock all media. If this fails, nothing is locked. */
13917 alock.release();
13918 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13919 alock.acquire();
13920 if (FAILED(rc))
13921 {
13922 mrc = setError(rc,
13923 tr("Locking of attached media failed"));
13924 }
13925 }
13926
13927 return mrc;
13928}
13929
13930/**
13931 * Undoes the locks made by by #lockMedia().
13932 */
13933void SessionMachine::unlockMedia()
13934{
13935 AutoCaller autoCaller(this);
13936 AssertComRCReturnVoid(autoCaller.rc());
13937
13938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13939
13940 /* we may be holding important error info on the current thread;
13941 * preserve it */
13942 ErrorInfoKeeper eik;
13943
13944 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13945 AssertComRC(rc);
13946}
13947
13948/**
13949 * Helper to change the machine state (reimplementation).
13950 *
13951 * @note Locks this object for writing.
13952 * @note This method must not call saveSettings or SaveSettings, otherwise
13953 * it can cause crashes in random places due to unexpectedly committing
13954 * the current settings. The caller is responsible for that. The call
13955 * to saveStateSettings is fine, because this method does not commit.
13956 */
13957HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
13958{
13959 LogFlowThisFuncEnter();
13960 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
13961
13962 AutoCaller autoCaller(this);
13963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13964
13965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13966
13967 MachineState_T oldMachineState = mData->mMachineState;
13968
13969 AssertMsgReturn(oldMachineState != aMachineState,
13970 ("oldMachineState=%s, aMachineState=%s\n",
13971 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
13972 E_FAIL);
13973
13974 HRESULT rc = S_OK;
13975
13976 int stsFlags = 0;
13977 bool deleteSavedState = false;
13978
13979 /* detect some state transitions */
13980
13981 if ( ( oldMachineState == MachineState_Saved
13982 && aMachineState == MachineState_Restoring)
13983 || ( ( oldMachineState == MachineState_PoweredOff
13984 || oldMachineState == MachineState_Teleported
13985 || oldMachineState == MachineState_Aborted
13986 )
13987 && ( aMachineState == MachineState_TeleportingIn
13988 || aMachineState == MachineState_Starting
13989 )
13990 )
13991 )
13992 {
13993 /* The EMT thread is about to start */
13994
13995 /* Nothing to do here for now... */
13996
13997 /// @todo NEWMEDIA don't let mDVDDrive and other children
13998 /// change anything when in the Starting/Restoring state
13999 }
14000 else if ( ( oldMachineState == MachineState_Running
14001 || oldMachineState == MachineState_Paused
14002 || oldMachineState == MachineState_Teleporting
14003 || oldMachineState == MachineState_LiveSnapshotting
14004 || oldMachineState == MachineState_Stuck
14005 || oldMachineState == MachineState_Starting
14006 || oldMachineState == MachineState_Stopping
14007 || oldMachineState == MachineState_Saving
14008 || oldMachineState == MachineState_Restoring
14009 || oldMachineState == MachineState_TeleportingPausedVM
14010 || oldMachineState == MachineState_TeleportingIn
14011 )
14012 && ( aMachineState == MachineState_PoweredOff
14013 || aMachineState == MachineState_Saved
14014 || aMachineState == MachineState_Teleported
14015 || aMachineState == MachineState_Aborted
14016 )
14017 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14018 * snapshot */
14019 && ( mConsoleTaskData.mSnapshot.isNull()
14020 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14021 )
14022 )
14023 {
14024 /* The EMT thread has just stopped, unlock attached media. Note that as
14025 * opposed to locking that is done from Console, we do unlocking here
14026 * because the VM process may have aborted before having a chance to
14027 * properly unlock all media it locked. */
14028
14029 unlockMedia();
14030 }
14031
14032 if (oldMachineState == MachineState_Restoring)
14033 {
14034 if (aMachineState != MachineState_Saved)
14035 {
14036 /*
14037 * delete the saved state file once the machine has finished
14038 * restoring from it (note that Console sets the state from
14039 * Restoring to Saved if the VM couldn't restore successfully,
14040 * to give the user an ability to fix an error and retry --
14041 * we keep the saved state file in this case)
14042 */
14043 deleteSavedState = true;
14044 }
14045 }
14046 else if ( oldMachineState == MachineState_Saved
14047 && ( aMachineState == MachineState_PoweredOff
14048 || aMachineState == MachineState_Aborted
14049 || aMachineState == MachineState_Teleported
14050 )
14051 )
14052 {
14053 /*
14054 * delete the saved state after Console::ForgetSavedState() is called
14055 * or if the VM process (owning a direct VM session) crashed while the
14056 * VM was Saved
14057 */
14058
14059 /// @todo (dmik)
14060 // Not sure that deleting the saved state file just because of the
14061 // client death before it attempted to restore the VM is a good
14062 // thing. But when it crashes we need to go to the Aborted state
14063 // which cannot have the saved state file associated... The only
14064 // way to fix this is to make the Aborted condition not a VM state
14065 // but a bool flag: i.e., when a crash occurs, set it to true and
14066 // change the state to PoweredOff or Saved depending on the
14067 // saved state presence.
14068
14069 deleteSavedState = true;
14070 mData->mCurrentStateModified = TRUE;
14071 stsFlags |= SaveSTS_CurStateModified;
14072 }
14073
14074 if ( aMachineState == MachineState_Starting
14075 || aMachineState == MachineState_Restoring
14076 || aMachineState == MachineState_TeleportingIn
14077 )
14078 {
14079 /* set the current state modified flag to indicate that the current
14080 * state is no more identical to the state in the
14081 * current snapshot */
14082 if (!mData->mCurrentSnapshot.isNull())
14083 {
14084 mData->mCurrentStateModified = TRUE;
14085 stsFlags |= SaveSTS_CurStateModified;
14086 }
14087 }
14088
14089 if (deleteSavedState)
14090 {
14091 if (mRemoveSavedState)
14092 {
14093 Assert(!mSSData->strStateFilePath.isEmpty());
14094
14095 // it is safe to delete the saved state file if ...
14096 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14097 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14098 // ... none of the snapshots share the saved state file
14099 )
14100 RTFileDelete(mSSData->strStateFilePath.c_str());
14101 }
14102
14103 mSSData->strStateFilePath.setNull();
14104 stsFlags |= SaveSTS_StateFilePath;
14105 }
14106
14107 /* redirect to the underlying peer machine */
14108 mPeer->setMachineState(aMachineState);
14109
14110 if ( aMachineState == MachineState_PoweredOff
14111 || aMachineState == MachineState_Teleported
14112 || aMachineState == MachineState_Aborted
14113 || aMachineState == MachineState_Saved)
14114 {
14115 /* the machine has stopped execution
14116 * (or the saved state file was adopted) */
14117 stsFlags |= SaveSTS_StateTimeStamp;
14118 }
14119
14120 if ( ( oldMachineState == MachineState_PoweredOff
14121 || oldMachineState == MachineState_Aborted
14122 || oldMachineState == MachineState_Teleported
14123 )
14124 && aMachineState == MachineState_Saved)
14125 {
14126 /* the saved state file was adopted */
14127 Assert(!mSSData->strStateFilePath.isEmpty());
14128 stsFlags |= SaveSTS_StateFilePath;
14129 }
14130
14131#ifdef VBOX_WITH_GUEST_PROPS
14132 if ( aMachineState == MachineState_PoweredOff
14133 || aMachineState == MachineState_Aborted
14134 || aMachineState == MachineState_Teleported)
14135 {
14136 /* Make sure any transient guest properties get removed from the
14137 * property store on shutdown. */
14138
14139 HWData::GuestPropertyMap::const_iterator it;
14140 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14141 if (!fNeedsSaving)
14142 for (it = mHWData->mGuestProperties.begin();
14143 it != mHWData->mGuestProperties.end(); ++it)
14144 if ( (it->second.mFlags & guestProp::TRANSIENT)
14145 || (it->second.mFlags & guestProp::TRANSRESET))
14146 {
14147 fNeedsSaving = true;
14148 break;
14149 }
14150 if (fNeedsSaving)
14151 {
14152 mData->mCurrentStateModified = TRUE;
14153 stsFlags |= SaveSTS_CurStateModified;
14154 }
14155 }
14156#endif
14157
14158 rc = saveStateSettings(stsFlags);
14159
14160 if ( ( oldMachineState != MachineState_PoweredOff
14161 && oldMachineState != MachineState_Aborted
14162 && oldMachineState != MachineState_Teleported
14163 )
14164 && ( aMachineState == MachineState_PoweredOff
14165 || aMachineState == MachineState_Aborted
14166 || aMachineState == MachineState_Teleported
14167 )
14168 )
14169 {
14170 /* we've been shut down for any reason */
14171 /* no special action so far */
14172 }
14173
14174 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14175 LogFlowThisFuncLeave();
14176 return rc;
14177}
14178
14179/**
14180 * Sends the current machine state value to the VM process.
14181 *
14182 * @note Locks this object for reading, then calls a client process.
14183 */
14184HRESULT SessionMachine::updateMachineStateOnClient()
14185{
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 AssertReturn(!!mData, E_FAIL);
14193 directControl = mData->mSession.mDirectControl;
14194
14195 /* directControl may be already set to NULL here in #OnSessionEnd()
14196 * called too early by the direct session process while there is still
14197 * some operation (like deleting the snapshot) in progress. The client
14198 * process in this case is waiting inside Session::close() for the
14199 * "end session" process object to complete, while #uninit() called by
14200 * #checkForDeath() on the Watcher thread is waiting for the pending
14201 * operation to complete. For now, we accept this inconsistent behavior
14202 * and simply do nothing here. */
14203
14204 if (mData->mSession.mState == SessionState_Unlocking)
14205 return S_OK;
14206
14207 AssertReturn(!directControl.isNull(), E_FAIL);
14208 }
14209
14210 return directControl->UpdateMachineState(mData->mMachineState);
14211}
Note: See TracBrowser for help on using the repository browser.

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