VirtualBox

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

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

IMachine::VideoCaptureFps

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