VirtualBox

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

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

build fix

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