VirtualBox

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

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

More testbox debug hacks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 478.5 KB
Line 
1/* $Id: MachineImpl.cpp 45719 2013-04-24 19:48:33Z 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 = 640;
171 mVideoCaptureHeight = 480;
172 mVideoCaptureEnabled = false;
173
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExForceEnabled = false;
184#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
185 mHWVirtExExclusive = false;
186#else
187 mHWVirtExExclusive = true;
188#endif
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mSyntheticCpu = false;
196 mHPETEnabled = false;
197
198 /* default boot order: floppy - DVD - HDD */
199 mBootOrder[0] = DeviceType_Floppy;
200 mBootOrder[1] = DeviceType_DVD;
201 mBootOrder[2] = DeviceType_HardDisk;
202 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
203 mBootOrder[i] = DeviceType_Null;
204
205 mClipboardMode = ClipboardMode_Disabled;
206 mDragAndDropMode = DragAndDropMode_Disabled;
207 mGuestPropertyNotificationPatterns = "";
208
209 mFirmwareType = FirmwareType_BIOS;
210 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
211 mPointingHIDType = PointingHIDType_PS2Mouse;
212 mChipsetType = ChipsetType_PIIX3;
213 mEmulatedUSBWebcamEnabled = FALSE;
214 mEmulatedUSBCardReaderEnabled = FALSE;
215
216 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++)
217 mCPUAttached[i] = false;
218
219 mIOCacheEnabled = true;
220 mIOCacheSize = 5; /* 5MB */
221
222 /* Maximum CPU execution cap by default. */
223 mCpuExecutionCap = 100;
224}
225
226Machine::HWData::~HWData()
227{
228}
229
230/////////////////////////////////////////////////////////////////////////////
231// Machine::HDData structure
232/////////////////////////////////////////////////////////////////////////////
233
234Machine::MediaData::MediaData()
235{
236}
237
238Machine::MediaData::~MediaData()
239{
240}
241
242/////////////////////////////////////////////////////////////////////////////
243// Machine class
244/////////////////////////////////////////////////////////////////////////////
245
246// constructor / destructor
247/////////////////////////////////////////////////////////////////////////////
248
249Machine::Machine()
250 : mCollectorGuest(NULL),
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->applyDefaults(aOsType);
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->applyDefaults(aOsType);
355
356 /* Let the OS type select 64-bit ness. */
357 mHWData->mLongMode = aOsType->is64Bit()
358 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 }
360
361 /* At this point the changing of the current state modification
362 * flag is allowed. */
363 allowStateModification();
364
365 /* commit all changes made during the initialization */
366 commit();
367 }
368
369 /* Confirm a successful initialization when it's the case */
370 if (SUCCEEDED(rc))
371 {
372 if (mData->mAccessible)
373 autoInitSpan.setSucceeded();
374 else
375 autoInitSpan.setLimited();
376 }
377
378 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
379 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
380 mData->mRegistered,
381 mData->mAccessible,
382 rc));
383
384 LogFlowThisFuncLeave();
385
386 return rc;
387}
388
389/**
390 * Initializes a new instance with data from machine XML (formerly Init_Registered).
391 * Gets called in two modes:
392 *
393 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
394 * UUID is specified and we mark the machine as "registered";
395 *
396 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
397 * and the machine remains unregistered until RegisterMachine() is called.
398 *
399 * @param aParent Associated parent object
400 * @param aConfigFile Local file system path to the VM settings file (can
401 * be relative to the VirtualBox config directory).
402 * @param aId UUID of the machine or NULL (see above).
403 *
404 * @return Success indicator. if not S_OK, the machine object is invalid
405 */
406HRESULT Machine::initFromSettings(VirtualBox *aParent,
407 const Utf8Str &strConfigFile,
408 const Guid *aId)
409{
410 LogFlowThisFuncEnter();
411 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
412
413 /* Enclose the state transition NotReady->InInit->Ready */
414 AutoInitSpan autoInitSpan(this);
415 AssertReturn(autoInitSpan.isOk(), E_FAIL);
416
417 HRESULT rc = initImpl(aParent, strConfigFile);
418 if (FAILED(rc)) return rc;
419
420 if (aId)
421 {
422 // loading a registered VM:
423 unconst(mData->mUuid) = *aId;
424 mData->mRegistered = TRUE;
425 // now load the settings from XML:
426 rc = registeredInit();
427 // this calls initDataAndChildObjects() and loadSettings()
428 }
429 else
430 {
431 // opening an unregistered VM (VirtualBox::OpenMachine()):
432 rc = initDataAndChildObjects();
433
434 if (SUCCEEDED(rc))
435 {
436 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
437 mData->mAccessible = TRUE;
438
439 try
440 {
441 // load and parse machine XML; this will throw on XML or logic errors
442 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
443
444 // reject VM UUID duplicates, they can happen if someone
445 // tries to register an already known VM config again
446 if (aParent->findMachine(mData->pMachineConfigFile->uuid,
447 true /* fPermitInaccessible */,
448 false /* aDoSetError */,
449 NULL) != VBOX_E_OBJECT_NOT_FOUND)
450 {
451 throw setError(E_FAIL,
452 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
453 mData->m_strConfigFile.c_str());
454 }
455
456 // use UUID from machine config
457 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
458
459 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
460 NULL /* puuidRegistry */);
461 if (FAILED(rc)) throw rc;
462
463 /* At this point the changing of the current state modification
464 * flag is allowed. */
465 allowStateModification();
466
467 commit();
468 }
469 catch (HRESULT err)
470 {
471 /* we assume that error info is set by the thrower */
472 rc = err;
473 }
474 catch (...)
475 {
476 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
477 }
478 }
479 }
480
481 /* Confirm a successful initialization when it's the case */
482 if (SUCCEEDED(rc))
483 {
484 if (mData->mAccessible)
485 autoInitSpan.setSucceeded();
486 else
487 {
488 autoInitSpan.setLimited();
489
490 // uninit media from this machine's media registry, or else
491 // reloading the settings will fail
492 mParent->unregisterMachineMedia(getId());
493 }
494 }
495
496 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
497 "rc=%08X\n",
498 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
499 mData->mRegistered, mData->mAccessible, rc));
500
501 LogFlowThisFuncLeave();
502
503 return rc;
504}
505
506/**
507 * Initializes a new instance from a machine config that is already in memory
508 * (import OVF case). Since we are importing, the UUID in the machine
509 * config is ignored and we always generate a fresh one.
510 *
511 * @param strName Name for the new machine; this overrides what is specified in config and is used
512 * for the settings file as well.
513 * @param config Machine configuration loaded and parsed from XML.
514 *
515 * @return Success indicator. if not S_OK, the machine object is invalid
516 */
517HRESULT Machine::init(VirtualBox *aParent,
518 const Utf8Str &strName,
519 const settings::MachineConfigFile &config)
520{
521 LogFlowThisFuncEnter();
522
523 /* Enclose the state transition NotReady->InInit->Ready */
524 AutoInitSpan autoInitSpan(this);
525 AssertReturn(autoInitSpan.isOk(), E_FAIL);
526
527 Utf8Str strConfigFile;
528 aParent->getDefaultMachineFolder(strConfigFile);
529 strConfigFile.append(RTPATH_DELIMITER);
530 strConfigFile.append(strName);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(".vbox");
534
535 HRESULT rc = initImpl(aParent, strConfigFile);
536 if (FAILED(rc)) return rc;
537
538 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */);
539 if (FAILED(rc)) return rc;
540
541 rc = initDataAndChildObjects();
542
543 if (SUCCEEDED(rc))
544 {
545 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
546 mData->mAccessible = TRUE;
547
548 // create empty machine config for instance data
549 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
550
551 // generate fresh UUID, ignore machine config
552 unconst(mData->mUuid).create();
553
554 rc = loadMachineDataFromSettings(config,
555 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
556
557 // override VM name as well, it may be different
558 mUserData->s.strName = strName;
559
560 if (SUCCEEDED(rc))
561 {
562 /* At this point the changing of the current state modification
563 * flag is allowed. */
564 allowStateModification();
565
566 /* commit all changes made during the initialization */
567 commit();
568 }
569 }
570
571 /* Confirm a successful initialization when it's the case */
572 if (SUCCEEDED(rc))
573 {
574 if (mData->mAccessible)
575 autoInitSpan.setSucceeded();
576 else
577 {
578 autoInitSpan.setLimited();
579
580 // uninit media from this machine's media registry, or else
581 // reloading the settings will fail
582 mParent->unregisterMachineMedia(getId());
583 }
584 }
585
586 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
587 "rc=%08X\n",
588 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
589 mData->mRegistered, mData->mAccessible, rc));
590
591 LogFlowThisFuncLeave();
592
593 return rc;
594}
595
596/**
597 * Shared code between the various init() implementations.
598 * @param aParent
599 * @return
600 */
601HRESULT Machine::initImpl(VirtualBox *aParent,
602 const Utf8Str &strConfigFile)
603{
604 LogFlowThisFuncEnter();
605
606 AssertReturn(aParent, E_INVALIDARG);
607 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
608
609 HRESULT rc = S_OK;
610
611 /* share the parent weakly */
612 unconst(mParent) = aParent;
613
614 /* allocate the essential machine data structure (the rest will be
615 * allocated later by initDataAndChildObjects() */
616 mData.allocate();
617
618 /* memorize the config file name (as provided) */
619 mData->m_strConfigFile = strConfigFile;
620
621 /* get the full file name */
622 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
623 if (RT_FAILURE(vrc1))
624 return setError(VBOX_E_FILE_ERROR,
625 tr("Invalid machine settings file name '%s' (%Rrc)"),
626 strConfigFile.c_str(),
627 vrc1);
628
629 LogFlowThisFuncLeave();
630
631 return rc;
632}
633
634/**
635 * Tries to create a machine settings file in the path stored in the machine
636 * instance data. Used when a new machine is created to fail gracefully if
637 * the settings file could not be written (e.g. because machine dir is read-only).
638 * @return
639 */
640HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite)
641{
642 HRESULT rc = S_OK;
643
644 // when we create a new machine, we must be able to create the settings file
645 RTFILE f = NIL_RTFILE;
646 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
647 if ( RT_SUCCESS(vrc)
648 || vrc == VERR_SHARING_VIOLATION
649 )
650 {
651 if (RT_SUCCESS(vrc))
652 RTFileClose(f);
653 if (!fForceOverwrite)
654 rc = setError(VBOX_E_FILE_ERROR,
655 tr("Machine settings file '%s' already exists"),
656 mData->m_strConfigFileFull.c_str());
657 else
658 {
659 /* try to delete the config file, as otherwise the creation
660 * of a new settings file will fail. */
661 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
662 if (RT_FAILURE(vrc2))
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Could not delete the existing settings file '%s' (%Rrc)"),
665 mData->m_strConfigFileFull.c_str(), vrc2);
666 }
667 }
668 else if ( vrc != VERR_FILE_NOT_FOUND
669 && vrc != VERR_PATH_NOT_FOUND
670 )
671 rc = setError(VBOX_E_FILE_ERROR,
672 tr("Invalid machine settings file name '%s' (%Rrc)"),
673 mData->m_strConfigFileFull.c_str(),
674 vrc);
675 return rc;
676}
677
678/**
679 * Initializes the registered machine by loading the settings file.
680 * This method is separated from #init() in order to make it possible to
681 * retry the operation after VirtualBox startup instead of refusing to
682 * startup the whole VirtualBox server in case if the settings file of some
683 * registered VM is invalid or inaccessible.
684 *
685 * @note Must be always called from this object's write lock
686 * (unless called from #init() that doesn't need any locking).
687 * @note Locks the mUSBController method for writing.
688 * @note Subclasses must not call this method.
689 */
690HRESULT Machine::registeredInit()
691{
692 AssertReturn(!isSessionMachine(), E_FAIL);
693 AssertReturn(!isSnapshotMachine(), E_FAIL);
694 AssertReturn(mData->mUuid.isValid(), E_FAIL);
695 AssertReturn(!mData->mAccessible, E_FAIL);
696
697 HRESULT rc = initDataAndChildObjects();
698
699 if (SUCCEEDED(rc))
700 {
701 /* Temporarily reset the registered flag in order to let setters
702 * potentially called from loadSettings() succeed (isMutable() used in
703 * all setters will return FALSE for a Machine instance if mRegistered
704 * is TRUE). */
705 mData->mRegistered = FALSE;
706
707 try
708 {
709 // load and parse machine XML; this will throw on XML or logic errors
710 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
711
712 if (mData->mUuid != mData->pMachineConfigFile->uuid)
713 throw setError(E_FAIL,
714 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
715 mData->pMachineConfigFile->uuid.raw(),
716 mData->m_strConfigFileFull.c_str(),
717 mData->mUuid.toString().c_str(),
718 mParent->settingsFilePath().c_str());
719
720 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile,
721 NULL /* const Guid *puuidRegistry */);
722 if (FAILED(rc)) throw rc;
723 }
724 catch (HRESULT err)
725 {
726 /* we assume that error info is set by the thrower */
727 rc = err;
728 }
729 catch (...)
730 {
731 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
732 }
733
734 /* Restore the registered flag (even on failure) */
735 mData->mRegistered = TRUE;
736 }
737
738 if (SUCCEEDED(rc))
739 {
740 /* Set mAccessible to TRUE only if we successfully locked and loaded
741 * the settings file */
742 mData->mAccessible = TRUE;
743
744 /* commit all changes made during loading the settings file */
745 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
746 /// @todo r=klaus for some reason the settings loading logic backs up
747 // the settings, and therefore a commit is needed. Should probably be changed.
748 }
749 else
750 {
751 /* If the machine is registered, then, instead of returning a
752 * failure, we mark it as inaccessible and set the result to
753 * success to give it a try later */
754
755 /* fetch the current error info */
756 mData->mAccessError = com::ErrorInfo();
757 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n",
758 mData->mUuid.raw(),
759 mData->mAccessError.getText().raw()));
760
761 /* rollback all changes */
762 rollback(false /* aNotify */);
763
764 // uninit media from this machine's media registry, or else
765 // reloading the settings will fail
766 mParent->unregisterMachineMedia(getId());
767
768 /* uninitialize the common part to make sure all data is reset to
769 * default (null) values */
770 uninitDataAndChildObjects();
771
772 rc = S_OK;
773 }
774
775 return rc;
776}
777
778/**
779 * Uninitializes the instance.
780 * Called either from FinalRelease() or by the parent when it gets destroyed.
781 *
782 * @note The caller of this method must make sure that this object
783 * a) doesn't have active callers on the current thread and b) is not locked
784 * by the current thread; otherwise uninit() will hang either a) due to
785 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
786 * a dead-lock caused by this thread waiting for all callers on the other
787 * threads are done but preventing them from doing so by holding a lock.
788 */
789void Machine::uninit()
790{
791 LogFlowThisFuncEnter();
792
793 Assert(!isWriteLockOnCurrentThread());
794
795 Assert(!uRegistryNeedsSaving);
796 if (uRegistryNeedsSaving)
797 {
798 AutoCaller autoCaller(this);
799 if (SUCCEEDED(autoCaller.rc()))
800 {
801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
802 saveSettings(NULL, Machine::SaveS_Force);
803 }
804 }
805
806 /* Enclose the state transition Ready->InUninit->NotReady */
807 AutoUninitSpan autoUninitSpan(this);
808 if (autoUninitSpan.uninitDone())
809 return;
810
811 Assert(!isSnapshotMachine());
812 Assert(!isSessionMachine());
813 Assert(!!mData);
814
815 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
816 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
817
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819
820 if (!mData->mSession.mMachine.isNull())
821 {
822 /* Theoretically, this can only happen if the VirtualBox server has been
823 * terminated while there were clients running that owned open direct
824 * sessions. Since in this case we are definitely called by
825 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
826 * won't happen on the client watcher thread (because it does
827 * VirtualBox::addCaller() for the duration of the
828 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
829 * cannot happen until the VirtualBox caller is released). This is
830 * important, because SessionMachine::uninit() cannot correctly operate
831 * after we return from this method (it expects the Machine instance is
832 * still valid). We'll call it ourselves below.
833 */
834 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
835 (SessionMachine*)mData->mSession.mMachine));
836
837 if (Global::IsOnlineOrTransient(mData->mMachineState))
838 {
839 LogWarningThisFunc(("Setting state to Aborted!\n"));
840 /* set machine state using SessionMachine reimplementation */
841 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted);
842 }
843
844 /*
845 * Uninitialize SessionMachine using public uninit() to indicate
846 * an unexpected uninitialization.
847 */
848 mData->mSession.mMachine->uninit();
849 /* SessionMachine::uninit() must set mSession.mMachine to null */
850 Assert(mData->mSession.mMachine.isNull());
851 }
852
853 // uninit media from this machine's media registry, if they're still there
854 Guid uuidMachine(getId());
855
856 /* the lock is no more necessary (SessionMachine is uninitialized) */
857 alock.release();
858
859 /* XXX This will fail with
860 * "cannot be closed because it is still attached to 1 virtual machines"
861 * because at this point we did not call uninitDataAndChildObjects() yet
862 * and therefore also removeBackReference() for all these mediums was not called! */
863
864 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
865 mParent->unregisterMachineMedia(uuidMachine);
866
867 // has machine been modified?
868 if (mData->flModifications)
869 {
870 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
871 rollback(false /* aNotify */);
872 }
873
874 if (mData->mAccessible)
875 uninitDataAndChildObjects();
876
877 /* free the essential data structure last */
878 mData.free();
879
880 LogFlowThisFuncLeave();
881}
882
883// IMachine properties
884/////////////////////////////////////////////////////////////////////////////
885
886STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent)
887{
888 CheckComArgOutPointerValid(aParent);
889
890 AutoLimitedCaller autoCaller(this);
891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
892
893 /* mParent is constant during life time, no need to lock */
894 ComObjPtr<VirtualBox> pVirtualBox(mParent);
895 pVirtualBox.queryInterfaceTo(aParent);
896
897 return S_OK;
898}
899
900STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible)
901{
902 CheckComArgOutPointerValid(aAccessible);
903
904 AutoLimitedCaller autoCaller(this);
905 if (FAILED(autoCaller.rc())) return autoCaller.rc();
906
907 LogFlowThisFunc(("ENTER\n"));
908
909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
910
911 HRESULT rc = S_OK;
912
913 if (!mData->mAccessible)
914 {
915 /* try to initialize the VM once more if not accessible */
916
917 AutoReinitSpan autoReinitSpan(this);
918 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
919
920#ifdef DEBUG
921 LogFlowThisFunc(("Dumping media backreferences\n"));
922 mParent->dumpAllBackRefs();
923#endif
924
925 if (mData->pMachineConfigFile)
926 {
927 // reset the XML file to force loadSettings() (called from registeredInit())
928 // to parse it again; the file might have changed
929 delete mData->pMachineConfigFile;
930 mData->pMachineConfigFile = NULL;
931 }
932
933 rc = registeredInit();
934
935 if (SUCCEEDED(rc) && mData->mAccessible)
936 {
937 autoReinitSpan.setSucceeded();
938
939 /* make sure interesting parties will notice the accessibility
940 * state change */
941 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState);
942 mParent->onMachineDataChange(mData->mUuid);
943 }
944 }
945
946 if (SUCCEEDED(rc))
947 *aAccessible = mData->mAccessible;
948
949 LogFlowThisFuncLeave();
950
951 return rc;
952}
953
954STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError)
955{
956 CheckComArgOutPointerValid(aAccessError);
957
958 AutoLimitedCaller autoCaller(this);
959 if (FAILED(autoCaller.rc())) return autoCaller.rc();
960
961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
962
963 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
964 {
965 /* return shortly */
966 aAccessError = NULL;
967 return S_OK;
968 }
969
970 HRESULT rc = S_OK;
971
972 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
973 rc = errorInfo.createObject();
974 if (SUCCEEDED(rc))
975 {
976 errorInfo->init(mData->mAccessError.getResultCode(),
977 mData->mAccessError.getInterfaceID().ref(),
978 Utf8Str(mData->mAccessError.getComponent()).c_str(),
979 Utf8Str(mData->mAccessError.getText()));
980 rc = errorInfo.queryInterfaceTo(aAccessError);
981 }
982
983 return rc;
984}
985
986STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName)
987{
988 CheckComArgOutPointerValid(aName);
989
990 AutoCaller autoCaller(this);
991 if (FAILED(autoCaller.rc())) return autoCaller.rc();
992
993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
994
995 mUserData->s.strName.cloneTo(aName);
996
997 return S_OK;
998}
999
1000STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
1001{
1002 CheckComArgStrNotEmptyOrNull(aName);
1003
1004 AutoCaller autoCaller(this);
1005 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1006
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = checkStateDependency(MutableStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription)
1027{
1028 CheckComArgOutPointerValid(aDescription);
1029
1030 AutoCaller autoCaller(this);
1031 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1032
1033 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1034
1035 mUserData->s.strDescription.cloneTo(aDescription);
1036
1037 return S_OK;
1038}
1039
1040STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription)
1041{
1042 AutoCaller autoCaller(this);
1043 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1044
1045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1046
1047 // this can be done in principle in any state as it doesn't affect the VM
1048 // significantly, but play safe by not messing around while complex
1049 // activities are going on
1050 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
1051 if (FAILED(rc)) return rc;
1052
1053 setModified(IsModified_MachineData);
1054 mUserData.backup();
1055 mUserData->s.strDescription = aDescription;
1056
1057 return S_OK;
1058}
1059
1060STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId)
1061{
1062 CheckComArgOutPointerValid(aId);
1063
1064 AutoLimitedCaller autoCaller(this);
1065 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1066
1067 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1068
1069 mData->mUuid.toUtf16().cloneTo(aId);
1070
1071 return S_OK;
1072}
1073
1074STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups))
1075{
1076 CheckComArgOutSafeArrayPointerValid(aGroups);
1077
1078 AutoCaller autoCaller(this);
1079 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1080
1081 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1082 SafeArray<BSTR> groups(mUserData->s.llGroups.size());
1083 size_t i = 0;
1084 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1085 it != mUserData->s.llGroups.end();
1086 ++it, i++)
1087 {
1088 Bstr tmp = *it;
1089 tmp.cloneTo(&groups[i]);
1090 }
1091 groups.detachTo(ComSafeArrayOutArg(aGroups));
1092
1093 return S_OK;
1094}
1095
1096STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups))
1097{
1098 AutoCaller autoCaller(this);
1099 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1100
1101 StringsList llGroups;
1102 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups);
1103 if (FAILED(rc))
1104 return rc;
1105
1106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1107
1108 // changing machine groups is possible while the VM is offline
1109 rc = checkStateDependency(OfflineStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.llGroups = llGroups;
1115
1116 return S_OK;
1117}
1118
1119STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId)
1120{
1121 CheckComArgOutPointerValid(aOSTypeId);
1122
1123 AutoCaller autoCaller(this);
1124 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1125
1126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1127
1128 mUserData->s.strOsType.cloneTo(aOSTypeId);
1129
1130 return S_OK;
1131}
1132
1133STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId)
1134{
1135 CheckComArgStrNotEmptyOrNull(aOSTypeId);
1136
1137 AutoCaller autoCaller(this);
1138 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1139
1140 /* look up the object by Id to check it is valid */
1141 ComPtr<IGuestOSType> guestOSType;
1142 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam());
1143 if (FAILED(rc)) return rc;
1144
1145 /* when setting, always use the "etalon" value for consistency -- lookup
1146 * by ID is case-insensitive and the input value may have different case */
1147 Bstr osTypeId;
1148 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1149 if (FAILED(rc)) return rc;
1150
1151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1152
1153 rc = checkStateDependency(MutableStateDep);
1154 if (FAILED(rc)) return rc;
1155
1156 setModified(IsModified_MachineData);
1157 mUserData.backup();
1158 mUserData->s.strOsType = osTypeId;
1159
1160 return S_OK;
1161}
1162
1163
1164STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType)
1165{
1166 CheckComArgOutPointerValid(aFirmwareType);
1167
1168 AutoCaller autoCaller(this);
1169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1170
1171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 *aFirmwareType = mHWData->mFirmwareType;
1174
1175 return S_OK;
1176}
1177
1178STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType)
1179{
1180 AutoCaller autoCaller(this);
1181 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 HRESULT rc = checkStateDependency(MutableStateDep);
1185 if (FAILED(rc)) return rc;
1186
1187 setModified(IsModified_MachineData);
1188 mHWData.backup();
1189 mHWData->mFirmwareType = aFirmwareType;
1190
1191 return S_OK;
1192}
1193
1194STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType)
1195{
1196 CheckComArgOutPointerValid(aKeyboardHIDType);
1197
1198 AutoCaller autoCaller(this);
1199 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1200
1201 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1202
1203 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1204
1205 return S_OK;
1206}
1207
1208STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType)
1209{
1210 AutoCaller autoCaller(this);
1211 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1213
1214 HRESULT rc = checkStateDependency(MutableStateDep);
1215 if (FAILED(rc)) return rc;
1216
1217 setModified(IsModified_MachineData);
1218 mHWData.backup();
1219 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1220
1221 return S_OK;
1222}
1223
1224STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType)
1225{
1226 CheckComArgOutPointerValid(aPointingHIDType);
1227
1228 AutoCaller autoCaller(this);
1229 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1230
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 *aPointingHIDType = mHWData->mPointingHIDType;
1234
1235 return S_OK;
1236}
1237
1238STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType)
1239{
1240 AutoCaller autoCaller(this);
1241 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mPointingHIDType = aPointingHIDType;
1250
1251 return S_OK;
1252}
1253
1254STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType)
1255{
1256 CheckComArgOutPointerValid(aChipsetType);
1257
1258 AutoCaller autoCaller(this);
1259 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1260
1261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 *aChipsetType = mHWData->mChipsetType;
1264
1265 return S_OK;
1266}
1267
1268STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType)
1269{
1270 AutoCaller autoCaller(this);
1271 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 HRESULT rc = checkStateDependency(MutableStateDep);
1275 if (FAILED(rc)) return rc;
1276
1277 if (aChipsetType != mHWData->mChipsetType)
1278 {
1279 setModified(IsModified_MachineData);
1280 mHWData.backup();
1281 mHWData->mChipsetType = aChipsetType;
1282
1283 // Resize network adapter array, to be finalized on commit/rollback.
1284 // We must not throw away entries yet, otherwise settings are lost
1285 // without a way to roll back.
1286 uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1287 uint32_t oldCount = mNetworkAdapters.size();
1288 if (newCount > oldCount)
1289 {
1290 mNetworkAdapters.resize(newCount);
1291 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1292 {
1293 unconst(mNetworkAdapters[slot]).createObject();
1294 mNetworkAdapters[slot]->init(this, slot);
1295 }
1296 }
1297 }
1298
1299 return S_OK;
1300}
1301
1302STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion)
1303{
1304 CheckComArgOutPointerValid(aHWVersion);
1305
1306 AutoCaller autoCaller(this);
1307 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1308
1309 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1310
1311 mHWData->mHWVersion.cloneTo(aHWVersion);
1312
1313 return S_OK;
1314}
1315
1316STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion)
1317{
1318 /* check known version */
1319 Utf8Str hwVersion = aHWVersion;
1320 if ( hwVersion.compare("1") != 0
1321 && hwVersion.compare("2") != 0)
1322 return setError(E_INVALIDARG,
1323 tr("Invalid hardware version: %ls\n"), aHWVersion);
1324
1325 AutoCaller autoCaller(this);
1326 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1327
1328 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1329
1330 HRESULT rc = checkStateDependency(MutableStateDep);
1331 if (FAILED(rc)) return rc;
1332
1333 setModified(IsModified_MachineData);
1334 mHWData.backup();
1335 mHWData->mHWVersion = hwVersion;
1336
1337 return S_OK;
1338}
1339
1340STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID)
1341{
1342 CheckComArgOutPointerValid(aUUID);
1343
1344 AutoCaller autoCaller(this);
1345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1346
1347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1348
1349 if (mHWData->mHardwareUUID.isValid())
1350 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID);
1351 else
1352 mData->mUuid.toUtf16().cloneTo(aUUID);
1353
1354 return S_OK;
1355}
1356
1357STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID)
1358{
1359 Guid hardwareUUID(aUUID);
1360 if (!hardwareUUID.isValid())
1361 return E_INVALIDARG;
1362
1363 AutoCaller autoCaller(this);
1364 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1365
1366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1367
1368 HRESULT rc = checkStateDependency(MutableStateDep);
1369 if (FAILED(rc)) return rc;
1370
1371 setModified(IsModified_MachineData);
1372 mHWData.backup();
1373 if (hardwareUUID == mData->mUuid)
1374 mHWData->mHardwareUUID.clear();
1375 else
1376 mHWData->mHardwareUUID = hardwareUUID;
1377
1378 return S_OK;
1379}
1380
1381STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize)
1382{
1383 CheckComArgOutPointerValid(memorySize);
1384
1385 AutoCaller autoCaller(this);
1386 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1387
1388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1389
1390 *memorySize = mHWData->mMemorySize;
1391
1392 return S_OK;
1393}
1394
1395STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize)
1396{
1397 /* check RAM limits */
1398 if ( memorySize < MM_RAM_MIN_IN_MB
1399 || memorySize > MM_RAM_MAX_IN_MB
1400 )
1401 return setError(E_INVALIDARG,
1402 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1403 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1404
1405 AutoCaller autoCaller(this);
1406 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1407
1408 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 HRESULT rc = checkStateDependency(MutableStateDep);
1411 if (FAILED(rc)) return rc;
1412
1413 setModified(IsModified_MachineData);
1414 mHWData.backup();
1415 mHWData->mMemorySize = memorySize;
1416
1417 return S_OK;
1418}
1419
1420STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount)
1421{
1422 CheckComArgOutPointerValid(CPUCount);
1423
1424 AutoCaller autoCaller(this);
1425 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1426
1427 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1428
1429 *CPUCount = mHWData->mCPUCount;
1430
1431 return S_OK;
1432}
1433
1434STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
1435{
1436 /* check CPU limits */
1437 if ( CPUCount < SchemaDefs::MinCPUCount
1438 || CPUCount > SchemaDefs::MaxCPUCount
1439 )
1440 return setError(E_INVALIDARG,
1441 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1442 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1443
1444 AutoCaller autoCaller(this);
1445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1446
1447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1448
1449 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1450 if (mHWData->mCPUHotPlugEnabled)
1451 {
1452 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1453 {
1454 if (mHWData->mCPUAttached[idx])
1455 return setError(E_INVALIDARG,
1456 tr("There is still a CPU attached to socket %lu."
1457 "Detach the CPU before removing the socket"),
1458 CPUCount, idx+1);
1459 }
1460 }
1461
1462 HRESULT rc = checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mCPUCount = CPUCount;
1468
1469 return S_OK;
1470}
1471
1472STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap)
1473{
1474 CheckComArgOutPointerValid(aExecutionCap);
1475
1476 AutoCaller autoCaller(this);
1477 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1478
1479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1480
1481 *aExecutionCap = mHWData->mCpuExecutionCap;
1482
1483 return S_OK;
1484}
1485
1486STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap)
1487{
1488 HRESULT rc = S_OK;
1489
1490 /* check throttle limits */
1491 if ( aExecutionCap < 1
1492 || aExecutionCap > 100
1493 )
1494 return setError(E_INVALIDARG,
1495 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1496 aExecutionCap, 1, 100);
1497
1498 AutoCaller autoCaller(this);
1499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1500
1501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1502
1503 alock.release();
1504 rc = onCPUExecutionCapChange(aExecutionCap);
1505 alock.acquire();
1506 if (FAILED(rc)) return rc;
1507
1508 setModified(IsModified_MachineData);
1509 mHWData.backup();
1510 mHWData->mCpuExecutionCap = aExecutionCap;
1511
1512 /* Save settings if online - todo why is this required?? */
1513 if (Global::IsOnline(mData->mMachineState))
1514 saveSettings(NULL);
1515
1516 return S_OK;
1517}
1518
1519
1520STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled)
1521{
1522 CheckComArgOutPointerValid(enabled);
1523
1524 AutoCaller autoCaller(this);
1525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1526
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *enabled = mHWData->mCPUHotPlugEnabled;
1530
1531 return S_OK;
1532}
1533
1534STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
1535{
1536 HRESULT rc = S_OK;
1537
1538 AutoCaller autoCaller(this);
1539 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1540
1541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1542
1543 rc = checkStateDependency(MutableStateDep);
1544 if (FAILED(rc)) return rc;
1545
1546 if (mHWData->mCPUHotPlugEnabled != enabled)
1547 {
1548 if (enabled)
1549 {
1550 setModified(IsModified_MachineData);
1551 mHWData.backup();
1552
1553 /* Add the amount of CPUs currently attached */
1554 for (unsigned i = 0; i < mHWData->mCPUCount; i++)
1555 {
1556 mHWData->mCPUAttached[i] = true;
1557 }
1558 }
1559 else
1560 {
1561 /*
1562 * We can disable hotplug only if the amount of maximum CPUs is equal
1563 * to the amount of attached CPUs
1564 */
1565 unsigned cCpusAttached = 0;
1566 unsigned iHighestId = 0;
1567
1568 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++)
1569 {
1570 if (mHWData->mCPUAttached[i])
1571 {
1572 cCpusAttached++;
1573 iHighestId = i;
1574 }
1575 }
1576
1577 if ( (cCpusAttached != mHWData->mCPUCount)
1578 || (iHighestId >= mHWData->mCPUCount))
1579 return setError(E_INVALIDARG,
1580 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1581
1582 setModified(IsModified_MachineData);
1583 mHWData.backup();
1584 }
1585 }
1586
1587 mHWData->mCPUHotPlugEnabled = enabled;
1588
1589 return rc;
1590}
1591
1592STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
1593{
1594#ifdef VBOX_WITH_USB_CARDREADER
1595 CheckComArgOutPointerValid(enabled);
1596
1597 AutoCaller autoCaller(this);
1598 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1599
1600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1601
1602 *enabled = mHWData->mEmulatedUSBCardReaderEnabled;
1603
1604 return S_OK;
1605#else
1606 NOREF(enabled);
1607 return E_NOTIMPL;
1608#endif
1609}
1610
1611STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
1612{
1613#ifdef VBOX_WITH_USB_CARDREADER
1614 AutoCaller autoCaller(this);
1615 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 HRESULT rc = checkStateDependency(MutableStateDep);
1619 if (FAILED(rc)) return rc;
1620
1621 setModified(IsModified_MachineData);
1622 mHWData.backup();
1623 mHWData->mEmulatedUSBCardReaderEnabled = enabled;
1624
1625 return S_OK;
1626#else
1627 NOREF(enabled);
1628 return E_NOTIMPL;
1629#endif
1630}
1631
1632STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
1633{
1634#ifdef VBOX_WITH_USB_VIDEO
1635 CheckComArgOutPointerValid(enabled);
1636
1637 AutoCaller autoCaller(this);
1638 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1639
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 *enabled = mHWData->mEmulatedUSBWebcamEnabled;
1643
1644 return S_OK;
1645#else
1646 NOREF(enabled);
1647 return E_NOTIMPL;
1648#endif
1649}
1650
1651STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
1652{
1653#ifdef VBOX_WITH_USB_VIDEO
1654 AutoCaller autoCaller(this);
1655 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1656 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1657
1658 HRESULT rc = checkStateDependency(MutableStateDep);
1659 if (FAILED(rc)) return rc;
1660
1661 setModified(IsModified_MachineData);
1662 mHWData.backup();
1663 mHWData->mEmulatedUSBWebcamEnabled = enabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(enabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *enabled)
1673{
1674 CheckComArgOutPointerValid(enabled);
1675
1676 AutoCaller autoCaller(this);
1677 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 *enabled = mHWData->mHPETEnabled;
1681
1682 return S_OK;
1683}
1684
1685STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled)
1686{
1687 HRESULT rc = S_OK;
1688
1689 AutoCaller autoCaller(this);
1690 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1692
1693 rc = checkStateDependency(MutableStateDep);
1694 if (FAILED(rc)) return rc;
1695
1696 setModified(IsModified_MachineData);
1697 mHWData.backup();
1698
1699 mHWData->mHPETEnabled = enabled;
1700
1701 return rc;
1702}
1703
1704STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled)
1705{
1706 AutoCaller autoCaller(this);
1707 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1708
1709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1710
1711 *fEnabled = mHWData->mVideoCaptureEnabled;
1712 return S_OK;
1713}
1714
1715STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled)
1716{
1717 AutoCaller autoCaller(this);
1718 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1719
1720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1721 mHWData->mVideoCaptureEnabled = fEnabled;
1722 return S_OK;
1723}
1724
1725STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile)
1726{
1727 AutoCaller autoCaller(this);
1728 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1729
1730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1731 mHWData->mVideoCaptureFile.cloneTo(apFile);
1732 return S_OK;
1733}
1734
1735STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile)
1736{
1737 Utf8Str strFile(aFile);
1738 AutoCaller autoCaller(this);
1739 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1740
1741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1742 if (strFile.isEmpty())
1743 strFile = "VideoCap.webm";
1744 mHWData->mVideoCaptureFile = strFile;
1745 return S_OK;
1746}
1747
1748
1749STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes)
1750{
1751 AutoCaller autoCaller(this);
1752 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1753
1754 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1755 *ulHorzRes = mHWData->mVideoCaptureWidth;
1756 return S_OK;
1757}
1758
1759STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes)
1760{
1761 AutoCaller autoCaller(this);
1762 if (FAILED(autoCaller.rc()))
1763 {
1764 LogFlow(("Autolocked failed\n"));
1765 return autoCaller.rc();
1766 }
1767
1768 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1769 mHWData->mVideoCaptureWidth = ulHorzRes;
1770 return S_OK;
1771}
1772
1773STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes)
1774{
1775 AutoCaller autoCaller(this);
1776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1777
1778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1779 *ulVertRes = mHWData->mVideoCaptureHeight;
1780 return S_OK;
1781}
1782
1783STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes)
1784{
1785 AutoCaller autoCaller(this);
1786 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1787
1788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1789 mHWData->mVideoCaptureHeight = ulVertRes;
1790 return S_OK;
1791}
1792
1793STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType)
1794{
1795 CheckComArgOutPointerValid(aGraphicsControllerType);
1796
1797 AutoCaller autoCaller(this);
1798 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1799
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1803
1804 return S_OK;
1805}
1806
1807STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType)
1808{
1809 switch (aGraphicsControllerType)
1810 {
1811 case GraphicsControllerType_Null:
1812 case GraphicsControllerType_VBoxVGA:
1813 break;
1814 default:
1815 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1816 }
1817
1818 AutoCaller autoCaller(this);
1819 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1820
1821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1822
1823 HRESULT rc = checkStateDependency(MutableStateDep);
1824 if (FAILED(rc)) return rc;
1825
1826 setModified(IsModified_MachineData);
1827 mHWData.backup();
1828 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1829
1830 return S_OK;
1831}
1832
1833STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize)
1834{
1835 CheckComArgOutPointerValid(memorySize);
1836
1837 AutoCaller autoCaller(this);
1838 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1839
1840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1841
1842 *memorySize = mHWData->mVRAMSize;
1843
1844 return S_OK;
1845}
1846
1847STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize)
1848{
1849 /* check VRAM limits */
1850 if (memorySize < SchemaDefs::MinGuestVRAM ||
1851 memorySize > SchemaDefs::MaxGuestVRAM)
1852 return setError(E_INVALIDARG,
1853 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1854 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1855
1856 AutoCaller autoCaller(this);
1857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1858
1859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 HRESULT rc = checkStateDependency(MutableStateDep);
1862 if (FAILED(rc)) return rc;
1863
1864 setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mVRAMSize = memorySize;
1867
1868 return S_OK;
1869}
1870
1871/** @todo this method should not be public */
1872STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize)
1873{
1874 CheckComArgOutPointerValid(memoryBalloonSize);
1875
1876 AutoCaller autoCaller(this);
1877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1878
1879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1880
1881 *memoryBalloonSize = mHWData->mMemoryBalloonSize;
1882
1883 return S_OK;
1884}
1885
1886/**
1887 * Set the memory balloon size.
1888 *
1889 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1890 * we have to make sure that we never call IGuest from here.
1891 */
1892STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize)
1893{
1894 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1895#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1896 /* check limits */
1897 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1898 return setError(E_INVALIDARG,
1899 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1900 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1901
1902 AutoCaller autoCaller(this);
1903 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1904
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 setModified(IsModified_MachineData);
1908 mHWData.backup();
1909 mHWData->mMemoryBalloonSize = memoryBalloonSize;
1910
1911 return S_OK;
1912#else
1913 NOREF(memoryBalloonSize);
1914 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1915#endif
1916}
1917
1918STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled)
1919{
1920 CheckComArgOutPointerValid(enabled);
1921
1922 AutoCaller autoCaller(this);
1923 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1924
1925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1926
1927 *enabled = mHWData->mPageFusionEnabled;
1928 return S_OK;
1929}
1930
1931STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled)
1932{
1933#ifdef VBOX_WITH_PAGE_SHARING
1934 AutoCaller autoCaller(this);
1935 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1936
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1940 setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mPageFusionEnabled = enabled;
1943 return S_OK;
1944#else
1945 NOREF(enabled);
1946 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1947#endif
1948}
1949
1950STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled)
1951{
1952 CheckComArgOutPointerValid(enabled);
1953
1954 AutoCaller autoCaller(this);
1955 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1956
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 *enabled = mHWData->mAccelerate3DEnabled;
1960
1961 return S_OK;
1962}
1963
1964STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable)
1965{
1966 AutoCaller autoCaller(this);
1967 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1968
1969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1970
1971 HRESULT rc = checkStateDependency(MutableStateDep);
1972 if (FAILED(rc)) return rc;
1973
1974 /** @todo check validity! */
1975
1976 setModified(IsModified_MachineData);
1977 mHWData.backup();
1978 mHWData->mAccelerate3DEnabled = enable;
1979
1980 return S_OK;
1981}
1982
1983
1984STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled)
1985{
1986 CheckComArgOutPointerValid(enabled);
1987
1988 AutoCaller autoCaller(this);
1989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
1990
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 *enabled = mHWData->mAccelerate2DVideoEnabled;
1994
1995 return S_OK;
1996}
1997
1998STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable)
1999{
2000 AutoCaller autoCaller(this);
2001 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2002
2003 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2004
2005 HRESULT rc = checkStateDependency(MutableStateDep);
2006 if (FAILED(rc)) return rc;
2007
2008 /** @todo check validity! */
2009
2010 setModified(IsModified_MachineData);
2011 mHWData.backup();
2012 mHWData->mAccelerate2DVideoEnabled = enable;
2013
2014 return S_OK;
2015}
2016
2017STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount)
2018{
2019 CheckComArgOutPointerValid(monitorCount);
2020
2021 AutoCaller autoCaller(this);
2022 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2023
2024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2025
2026 *monitorCount = mHWData->mMonitorCount;
2027
2028 return S_OK;
2029}
2030
2031STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount)
2032{
2033 /* make sure monitor count is a sensible number */
2034 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors)
2035 return setError(E_INVALIDARG,
2036 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2037 monitorCount, 1, SchemaDefs::MaxGuestMonitors);
2038
2039 AutoCaller autoCaller(this);
2040 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2041
2042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 HRESULT rc = checkStateDependency(MutableStateDep);
2045 if (FAILED(rc)) return rc;
2046
2047 setModified(IsModified_MachineData);
2048 mHWData.backup();
2049 mHWData->mMonitorCount = monitorCount;
2050
2051 return S_OK;
2052}
2053
2054STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings)
2055{
2056 CheckComArgOutPointerValid(biosSettings);
2057
2058 AutoCaller autoCaller(this);
2059 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2060
2061 /* mBIOSSettings is constant during life time, no need to lock */
2062 mBIOSSettings.queryInterfaceTo(biosSettings);
2063
2064 return S_OK;
2065}
2066
2067STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal)
2068{
2069 CheckComArgOutPointerValid(aVal);
2070
2071 AutoCaller autoCaller(this);
2072 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2073
2074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2075
2076 switch (property)
2077 {
2078 case CPUPropertyType_PAE:
2079 *aVal = mHWData->mPAEEnabled;
2080 break;
2081
2082 case CPUPropertyType_Synthetic:
2083 *aVal = mHWData->mSyntheticCpu;
2084 break;
2085
2086 case CPUPropertyType_LongMode:
2087 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2088 *aVal = TRUE;
2089 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2090 *aVal = FALSE;
2091#if HC_ARCH_BITS == 64
2092 else
2093 *aVal = TRUE;
2094#else
2095 else
2096 {
2097 *aVal = FALSE;
2098
2099 ComPtr<IGuestOSType> ptrGuestOSType;
2100 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2101 if (SUCCEEDED(hrc2))
2102 {
2103 BOOL fIs64Bit = FALSE;
2104 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2105 if (SUCCEEDED(hrc2) && fIs64Bit)
2106 {
2107 ComObjPtr<Host> ptrHost = mParent->host();
2108 alock.release();
2109
2110 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2);
2111 if (FAILED(hrc2))
2112 *aVal = FALSE;
2113 }
2114 }
2115 }
2116#endif
2117 break;
2118
2119 default:
2120 return E_INVALIDARG;
2121 }
2122 return S_OK;
2123}
2124
2125STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal)
2126{
2127 AutoCaller autoCaller(this);
2128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2129
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 switch (property)
2136 {
2137 case CPUPropertyType_PAE:
2138 setModified(IsModified_MachineData);
2139 mHWData.backup();
2140 mHWData->mPAEEnabled = !!aVal;
2141 break;
2142
2143 case CPUPropertyType_Synthetic:
2144 setModified(IsModified_MachineData);
2145 mHWData.backup();
2146 mHWData->mSyntheticCpu = !!aVal;
2147 break;
2148
2149 case CPUPropertyType_LongMode:
2150 setModified(IsModified_MachineData);
2151 mHWData.backup();
2152 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2153 break;
2154
2155 default:
2156 return E_INVALIDARG;
2157 }
2158 return S_OK;
2159}
2160
2161STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2162{
2163 CheckComArgOutPointerValid(aValEax);
2164 CheckComArgOutPointerValid(aValEbx);
2165 CheckComArgOutPointerValid(aValEcx);
2166 CheckComArgOutPointerValid(aValEdx);
2167
2168 AutoCaller autoCaller(this);
2169 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2170
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 switch(aId)
2174 {
2175 case 0x0:
2176 case 0x1:
2177 case 0x2:
2178 case 0x3:
2179 case 0x4:
2180 case 0x5:
2181 case 0x6:
2182 case 0x7:
2183 case 0x8:
2184 case 0x9:
2185 case 0xA:
2186 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2187 return E_INVALIDARG;
2188
2189 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2190 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2191 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2192 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2193 break;
2194
2195 case 0x80000000:
2196 case 0x80000001:
2197 case 0x80000002:
2198 case 0x80000003:
2199 case 0x80000004:
2200 case 0x80000005:
2201 case 0x80000006:
2202 case 0x80000007:
2203 case 0x80000008:
2204 case 0x80000009:
2205 case 0x8000000A:
2206 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2207 return E_INVALIDARG;
2208
2209 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2210 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2211 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2212 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2213 break;
2214
2215 default:
2216 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2217 }
2218 return S_OK;
2219}
2220
2221STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2222{
2223 AutoCaller autoCaller(this);
2224 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2225
2226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2227
2228 HRESULT rc = checkStateDependency(MutableStateDep);
2229 if (FAILED(rc)) return rc;
2230
2231 switch(aId)
2232 {
2233 case 0x0:
2234 case 0x1:
2235 case 0x2:
2236 case 0x3:
2237 case 0x4:
2238 case 0x5:
2239 case 0x6:
2240 case 0x7:
2241 case 0x8:
2242 case 0x9:
2243 case 0xA:
2244 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2245 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2246 setModified(IsModified_MachineData);
2247 mHWData.backup();
2248 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2249 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2250 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2251 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2252 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2253 break;
2254
2255 case 0x80000000:
2256 case 0x80000001:
2257 case 0x80000002:
2258 case 0x80000003:
2259 case 0x80000004:
2260 case 0x80000005:
2261 case 0x80000006:
2262 case 0x80000007:
2263 case 0x80000008:
2264 case 0x80000009:
2265 case 0x8000000A:
2266 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2267 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2268 setModified(IsModified_MachineData);
2269 mHWData.backup();
2270 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2271 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2272 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2273 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2274 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2275 break;
2276
2277 default:
2278 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2279 }
2280 return S_OK;
2281}
2282
2283STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId)
2284{
2285 AutoCaller autoCaller(this);
2286 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2287
2288 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2289
2290 HRESULT rc = checkStateDependency(MutableStateDep);
2291 if (FAILED(rc)) return rc;
2292
2293 switch(aId)
2294 {
2295 case 0x0:
2296 case 0x1:
2297 case 0x2:
2298 case 0x3:
2299 case 0x4:
2300 case 0x5:
2301 case 0x6:
2302 case 0x7:
2303 case 0x8:
2304 case 0x9:
2305 case 0xA:
2306 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2307 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2308 setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 /* Invalidate leaf. */
2311 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2312 break;
2313
2314 case 0x80000000:
2315 case 0x80000001:
2316 case 0x80000002:
2317 case 0x80000003:
2318 case 0x80000004:
2319 case 0x80000005:
2320 case 0x80000006:
2321 case 0x80000007:
2322 case 0x80000008:
2323 case 0x80000009:
2324 case 0x8000000A:
2325 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2326 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2327 setModified(IsModified_MachineData);
2328 mHWData.backup();
2329 /* Invalidate leaf. */
2330 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2331 break;
2332
2333 default:
2334 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2335 }
2336 return S_OK;
2337}
2338
2339STDMETHODIMP Machine::RemoveAllCPUIDLeaves()
2340{
2341 AutoCaller autoCaller(this);
2342 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2343
2344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2345
2346 HRESULT rc = checkStateDependency(MutableStateDep);
2347 if (FAILED(rc)) return rc;
2348
2349 setModified(IsModified_MachineData);
2350 mHWData.backup();
2351
2352 /* Invalidate all standard leafs. */
2353 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++)
2354 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2355
2356 /* Invalidate all extended leafs. */
2357 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++)
2358 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2359
2360 return S_OK;
2361}
2362
2363STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal)
2364{
2365 CheckComArgOutPointerValid(aVal);
2366
2367 AutoCaller autoCaller(this);
2368 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2369
2370 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 switch(property)
2373 {
2374 case HWVirtExPropertyType_Enabled:
2375 *aVal = mHWData->mHWVirtExEnabled;
2376 break;
2377
2378 case HWVirtExPropertyType_Exclusive:
2379 *aVal = mHWData->mHWVirtExExclusive;
2380 break;
2381
2382 case HWVirtExPropertyType_VPID:
2383 *aVal = mHWData->mHWVirtExVPIDEnabled;
2384 break;
2385
2386 case HWVirtExPropertyType_NestedPaging:
2387 *aVal = mHWData->mHWVirtExNestedPagingEnabled;
2388 break;
2389
2390 case HWVirtExPropertyType_LargePages:
2391 *aVal = mHWData->mHWVirtExLargePagesEnabled;
2392#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2393 *aVal = FALSE;
2394#endif
2395 break;
2396
2397 case HWVirtExPropertyType_Force:
2398 *aVal = mHWData->mHWVirtExForceEnabled;
2399 break;
2400
2401 default:
2402 return E_INVALIDARG;
2403 }
2404 return S_OK;
2405}
2406
2407STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal)
2408{
2409 AutoCaller autoCaller(this);
2410 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2411
2412 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2413
2414 HRESULT rc = checkStateDependency(MutableStateDep);
2415 if (FAILED(rc)) return rc;
2416
2417 switch(property)
2418 {
2419 case HWVirtExPropertyType_Enabled:
2420 setModified(IsModified_MachineData);
2421 mHWData.backup();
2422 mHWData->mHWVirtExEnabled = !!aVal;
2423 break;
2424
2425 case HWVirtExPropertyType_Exclusive:
2426 setModified(IsModified_MachineData);
2427 mHWData.backup();
2428 mHWData->mHWVirtExExclusive = !!aVal;
2429 break;
2430
2431 case HWVirtExPropertyType_VPID:
2432 setModified(IsModified_MachineData);
2433 mHWData.backup();
2434 mHWData->mHWVirtExVPIDEnabled = !!aVal;
2435 break;
2436
2437 case HWVirtExPropertyType_NestedPaging:
2438 setModified(IsModified_MachineData);
2439 mHWData.backup();
2440 mHWData->mHWVirtExNestedPagingEnabled = !!aVal;
2441 break;
2442
2443 case HWVirtExPropertyType_LargePages:
2444 setModified(IsModified_MachineData);
2445 mHWData.backup();
2446 mHWData->mHWVirtExLargePagesEnabled = !!aVal;
2447 break;
2448
2449 case HWVirtExPropertyType_Force:
2450 setModified(IsModified_MachineData);
2451 mHWData.backup();
2452 mHWData->mHWVirtExForceEnabled = !!aVal;
2453 break;
2454
2455 default:
2456 return E_INVALIDARG;
2457 }
2458
2459 return S_OK;
2460}
2461
2462STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder)
2463{
2464 CheckComArgOutPointerValid(aSnapshotFolder);
2465
2466 AutoCaller autoCaller(this);
2467 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2468
2469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2470
2471 Utf8Str strFullSnapshotFolder;
2472 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
2473 strFullSnapshotFolder.cloneTo(aSnapshotFolder);
2474
2475 return S_OK;
2476}
2477
2478STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder)
2479{
2480 /* @todo (r=dmik):
2481 * 1. Allow to change the name of the snapshot folder containing snapshots
2482 * 2. Rename the folder on disk instead of just changing the property
2483 * value (to be smart and not to leave garbage). Note that it cannot be
2484 * done here because the change may be rolled back. Thus, the right
2485 * place is #saveSettings().
2486 */
2487
2488 AutoCaller autoCaller(this);
2489 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2490
2491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2492
2493 HRESULT rc = checkStateDependency(MutableStateDep);
2494 if (FAILED(rc)) return rc;
2495
2496 if (!mData->mCurrentSnapshot.isNull())
2497 return setError(E_FAIL,
2498 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2499
2500 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original
2501
2502 Utf8Str strSnapshotFolder(strSnapshotFolder0);
2503 if (strSnapshotFolder.isEmpty())
2504 strSnapshotFolder = "Snapshots";
2505 int vrc = calculateFullPath(strSnapshotFolder,
2506 strSnapshotFolder);
2507 if (RT_FAILURE(vrc))
2508 return setError(E_FAIL,
2509 tr("Invalid snapshot folder '%ls' (%Rrc)"),
2510 aSnapshotFolder, vrc);
2511
2512 setModified(IsModified_MachineData);
2513 mUserData.backup();
2514
2515 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2516
2517 return S_OK;
2518}
2519
2520STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments))
2521{
2522 CheckComArgOutSafeArrayPointerValid(aAttachments);
2523
2524 AutoCaller autoCaller(this);
2525 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2526
2527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2528
2529 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments);
2530 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
2531
2532 return S_OK;
2533}
2534
2535STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer)
2536{
2537 CheckComArgOutPointerValid(vrdeServer);
2538
2539 AutoCaller autoCaller(this);
2540 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2541
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 Assert(!!mVRDEServer);
2545 mVRDEServer.queryInterfaceTo(vrdeServer);
2546
2547 return S_OK;
2548}
2549
2550STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter)
2551{
2552 CheckComArgOutPointerValid(audioAdapter);
2553
2554 AutoCaller autoCaller(this);
2555 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2556
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 mAudioAdapter.queryInterfaceTo(audioAdapter);
2560 return S_OK;
2561}
2562
2563STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController)
2564{
2565#ifdef VBOX_WITH_VUSB
2566 CheckComArgOutPointerValid(aUSBController);
2567
2568 AutoCaller autoCaller(this);
2569 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2570
2571 clearError();
2572 MultiResult rc(S_OK);
2573
2574# ifdef VBOX_WITH_USB
2575 rc = mParent->host()->checkUSBProxyService();
2576 if (FAILED(rc)) return rc;
2577# endif
2578
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 return rc = mUSBController.queryInterfaceTo(aUSBController);
2582#else
2583 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2584 * extended error info to indicate that USB is simply not available
2585 * (w/o treating it as a failure), for example, as in OSE */
2586 NOREF(aUSBController);
2587 ReturnComNotImplemented();
2588#endif /* VBOX_WITH_VUSB */
2589}
2590
2591STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath)
2592{
2593 CheckComArgOutPointerValid(aFilePath);
2594
2595 AutoLimitedCaller autoCaller(this);
2596 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2597
2598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 mData->m_strConfigFileFull.cloneTo(aFilePath);
2601 return S_OK;
2602}
2603
2604STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified)
2605{
2606 CheckComArgOutPointerValid(aModified);
2607
2608 AutoCaller autoCaller(this);
2609 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2610
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 HRESULT rc = checkStateDependency(MutableStateDep);
2614 if (FAILED(rc)) return rc;
2615
2616 if (!mData->pMachineConfigFile->fileExists())
2617 // this is a new machine, and no config file exists yet:
2618 *aModified = TRUE;
2619 else
2620 *aModified = (mData->flModifications != 0);
2621
2622 return S_OK;
2623}
2624
2625STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState)
2626{
2627 CheckComArgOutPointerValid(aSessionState);
2628
2629 AutoCaller autoCaller(this);
2630 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2631
2632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2633
2634 *aSessionState = mData->mSession.mState;
2635
2636 return S_OK;
2637}
2638
2639STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType)
2640{
2641 CheckComArgOutPointerValid(aSessionType);
2642
2643 AutoCaller autoCaller(this);
2644 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2645
2646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2647
2648 mData->mSession.mType.cloneTo(aSessionType);
2649
2650 return S_OK;
2651}
2652
2653STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID)
2654{
2655 CheckComArgOutPointerValid(aSessionPID);
2656
2657 AutoCaller autoCaller(this);
2658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2659
2660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2661
2662 *aSessionPID = mData->mSession.mPID;
2663
2664 return S_OK;
2665}
2666
2667STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState)
2668{
2669 CheckComArgOutPointerValid(machineState);
2670
2671 AutoCaller autoCaller(this);
2672 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2673
2674 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2675
2676 *machineState = mData->mMachineState;
2677
2678 return S_OK;
2679}
2680
2681STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange)
2682{
2683 CheckComArgOutPointerValid(aLastStateChange);
2684
2685 AutoCaller autoCaller(this);
2686 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2687
2688 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2689
2690 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2691
2692 return S_OK;
2693}
2694
2695STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
2696{
2697 CheckComArgOutPointerValid(aStateFilePath);
2698
2699 AutoCaller autoCaller(this);
2700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2701
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 mSSData->strStateFilePath.cloneTo(aStateFilePath);
2705
2706 return S_OK;
2707}
2708
2709STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder)
2710{
2711 CheckComArgOutPointerValid(aLogFolder);
2712
2713 AutoCaller autoCaller(this);
2714 AssertComRCReturnRC(autoCaller.rc());
2715
2716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2717
2718 Utf8Str logFolder;
2719 getLogFolder(logFolder);
2720 logFolder.cloneTo(aLogFolder);
2721
2722 return S_OK;
2723}
2724
2725STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot)
2726{
2727 CheckComArgOutPointerValid(aCurrentSnapshot);
2728
2729 AutoCaller autoCaller(this);
2730 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2731
2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2733
2734 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot);
2735
2736 return S_OK;
2737}
2738
2739STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount)
2740{
2741 CheckComArgOutPointerValid(aSnapshotCount);
2742
2743 AutoCaller autoCaller(this);
2744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2745
2746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2749 ? 0
2750 : mData->mFirstSnapshot->getAllChildrenCount() + 1;
2751
2752 return S_OK;
2753}
2754
2755STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified)
2756{
2757 CheckComArgOutPointerValid(aCurrentStateModified);
2758
2759 AutoCaller autoCaller(this);
2760 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2761
2762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2763
2764 /* Note: for machines with no snapshots, we always return FALSE
2765 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2766 * reasons :) */
2767
2768 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2769 ? FALSE
2770 : mData->mCurrentStateModified;
2771
2772 return S_OK;
2773}
2774
2775STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))
2776{
2777 CheckComArgOutSafeArrayPointerValid(aSharedFolders);
2778
2779 AutoCaller autoCaller(this);
2780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2781
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders);
2785 folders.detachTo(ComSafeArrayOutArg(aSharedFolders));
2786
2787 return S_OK;
2788}
2789
2790STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode)
2791{
2792 CheckComArgOutPointerValid(aClipboardMode);
2793
2794 AutoCaller autoCaller(this);
2795 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2796
2797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 *aClipboardMode = mHWData->mClipboardMode;
2800
2801 return S_OK;
2802}
2803
2804STDMETHODIMP
2805Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode)
2806{
2807 HRESULT rc = S_OK;
2808
2809 AutoCaller autoCaller(this);
2810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2811
2812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2813
2814 alock.release();
2815 rc = onClipboardModeChange(aClipboardMode);
2816 alock.acquire();
2817 if (FAILED(rc)) return rc;
2818
2819 setModified(IsModified_MachineData);
2820 mHWData.backup();
2821 mHWData->mClipboardMode = aClipboardMode;
2822
2823 /* Save settings if online - todo why is this required?? */
2824 if (Global::IsOnline(mData->mMachineState))
2825 saveSettings(NULL);
2826
2827 return S_OK;
2828}
2829
2830STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode)
2831{
2832 CheckComArgOutPointerValid(aDragAndDropMode);
2833
2834 AutoCaller autoCaller(this);
2835 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2836
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 *aDragAndDropMode = mHWData->mDragAndDropMode;
2840
2841 return S_OK;
2842}
2843
2844STDMETHODIMP
2845Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode)
2846{
2847 HRESULT rc = S_OK;
2848
2849 AutoCaller autoCaller(this);
2850 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2851
2852 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2853
2854 alock.release();
2855 rc = onDragAndDropModeChange(aDragAndDropMode);
2856 alock.acquire();
2857 if (FAILED(rc)) return rc;
2858
2859 setModified(IsModified_MachineData);
2860 mHWData.backup();
2861 mHWData->mDragAndDropMode = aDragAndDropMode;
2862
2863 /* Save settings if online - todo why is this required?? */
2864 if (Global::IsOnline(mData->mMachineState))
2865 saveSettings(NULL);
2866
2867 return S_OK;
2868}
2869
2870STDMETHODIMP
2871Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns)
2872{
2873 CheckComArgOutPointerValid(aPatterns);
2874
2875 AutoCaller autoCaller(this);
2876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2877
2878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2879
2880 try
2881 {
2882 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns);
2883 }
2884 catch (...)
2885 {
2886 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
2887 }
2888
2889 return S_OK;
2890}
2891
2892STDMETHODIMP
2893Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns)
2894{
2895 AutoCaller autoCaller(this);
2896 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2897
2898 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2899
2900 HRESULT rc = checkStateDependency(MutableStateDep);
2901 if (FAILED(rc)) return rc;
2902
2903 setModified(IsModified_MachineData);
2904 mHWData.backup();
2905 mHWData->mGuestPropertyNotificationPatterns = aPatterns;
2906 return rc;
2907}
2908
2909STDMETHODIMP
2910Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers))
2911{
2912 CheckComArgOutSafeArrayPointerValid(aStorageControllers);
2913
2914 AutoCaller autoCaller(this);
2915 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2916
2917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data());
2920 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers));
2921
2922 return S_OK;
2923}
2924
2925STDMETHODIMP
2926Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled)
2927{
2928 CheckComArgOutPointerValid(aEnabled);
2929
2930 AutoCaller autoCaller(this);
2931 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2932
2933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2934
2935 *aEnabled = mUserData->s.fTeleporterEnabled;
2936
2937 return S_OK;
2938}
2939
2940STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled)
2941{
2942 AutoCaller autoCaller(this);
2943 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2944
2945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 /* Only allow it to be set to true when PoweredOff or Aborted.
2948 (Clearing it is always permitted.) */
2949 if ( aEnabled
2950 && mData->mRegistered
2951 && ( !isSessionMachine()
2952 || ( mData->mMachineState != MachineState_PoweredOff
2953 && mData->mMachineState != MachineState_Teleported
2954 && mData->mMachineState != MachineState_Aborted
2955 )
2956 )
2957 )
2958 return setError(VBOX_E_INVALID_VM_STATE,
2959 tr("The machine is not powered off (state is %s)"),
2960 Global::stringifyMachineState(mData->mMachineState));
2961
2962 setModified(IsModified_MachineData);
2963 mUserData.backup();
2964 mUserData->s.fTeleporterEnabled = !!aEnabled;
2965
2966 return S_OK;
2967}
2968
2969STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort)
2970{
2971 CheckComArgOutPointerValid(aPort);
2972
2973 AutoCaller autoCaller(this);
2974 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2975
2976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 *aPort = (ULONG)mUserData->s.uTeleporterPort;
2979
2980 return S_OK;
2981}
2982
2983STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort)
2984{
2985 if (aPort >= _64K)
2986 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort);
2987
2988 AutoCaller autoCaller(this);
2989 if (FAILED(autoCaller.rc())) return autoCaller.rc();
2990
2991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2992
2993 HRESULT rc = checkStateDependency(MutableStateDep);
2994 if (FAILED(rc)) return rc;
2995
2996 setModified(IsModified_MachineData);
2997 mUserData.backup();
2998 mUserData->s.uTeleporterPort = (uint32_t)aPort;
2999
3000 return S_OK;
3001}
3002
3003STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress)
3004{
3005 CheckComArgOutPointerValid(aAddress);
3006
3007 AutoCaller autoCaller(this);
3008 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3009
3010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 mUserData->s.strTeleporterAddress.cloneTo(aAddress);
3013
3014 return S_OK;
3015}
3016
3017STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress)
3018{
3019 AutoCaller autoCaller(this);
3020 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3021
3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3023
3024 HRESULT rc = checkStateDependency(MutableStateDep);
3025 if (FAILED(rc)) return rc;
3026
3027 setModified(IsModified_MachineData);
3028 mUserData.backup();
3029 mUserData->s.strTeleporterAddress = aAddress;
3030
3031 return S_OK;
3032}
3033
3034STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword)
3035{
3036 CheckComArgOutPointerValid(aPassword);
3037
3038 AutoCaller autoCaller(this);
3039 HRESULT hrc = autoCaller.rc();
3040 if (SUCCEEDED(hrc))
3041 {
3042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3043 mUserData->s.strTeleporterPassword.cloneTo(aPassword);
3044 }
3045
3046 return hrc;
3047}
3048
3049STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword)
3050{
3051 /*
3052 * Hash the password first.
3053 */
3054 Utf8Str strPassword(aPassword);
3055 if (!strPassword.isEmpty())
3056 {
3057 if (VBoxIsPasswordHashed(&strPassword))
3058 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3059 VBoxHashPassword(&strPassword);
3060 }
3061
3062 /*
3063 * Do the update.
3064 */
3065 AutoCaller autoCaller(this);
3066 HRESULT hrc = autoCaller.rc();
3067 if (SUCCEEDED(hrc))
3068 {
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070 hrc = checkStateDependency(MutableStateDep);
3071 if (SUCCEEDED(hrc))
3072 {
3073 setModified(IsModified_MachineData);
3074 mUserData.backup();
3075 mUserData->s.strTeleporterPassword = strPassword;
3076 }
3077 }
3078
3079 return hrc;
3080}
3081
3082STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState)
3083{
3084 CheckComArgOutPointerValid(aState);
3085
3086 AutoCaller autoCaller(this);
3087 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3088
3089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3090
3091 *aState = mUserData->s.enmFaultToleranceState;
3092 return S_OK;
3093}
3094
3095STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState)
3096{
3097 AutoCaller autoCaller(this);
3098 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3099
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101
3102 /* @todo deal with running state change. */
3103 HRESULT rc = checkStateDependency(MutableStateDep);
3104 if (FAILED(rc)) return rc;
3105
3106 setModified(IsModified_MachineData);
3107 mUserData.backup();
3108 mUserData->s.enmFaultToleranceState = aState;
3109 return S_OK;
3110}
3111
3112STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress)
3113{
3114 CheckComArgOutPointerValid(aAddress);
3115
3116 AutoCaller autoCaller(this);
3117 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3118
3119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3120
3121 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress);
3122 return S_OK;
3123}
3124
3125STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress)
3126{
3127 AutoCaller autoCaller(this);
3128 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3129
3130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3131
3132 /* @todo deal with running state change. */
3133 HRESULT rc = checkStateDependency(MutableStateDep);
3134 if (FAILED(rc)) return rc;
3135
3136 setModified(IsModified_MachineData);
3137 mUserData.backup();
3138 mUserData->s.strFaultToleranceAddress = aAddress;
3139 return S_OK;
3140}
3141
3142STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort)
3143{
3144 CheckComArgOutPointerValid(aPort);
3145
3146 AutoCaller autoCaller(this);
3147 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3148
3149 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 *aPort = mUserData->s.uFaultTolerancePort;
3152 return S_OK;
3153}
3154
3155STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort)
3156{
3157 AutoCaller autoCaller(this);
3158 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3159
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 /* @todo deal with running state change. */
3163 HRESULT rc = checkStateDependency(MutableStateDep);
3164 if (FAILED(rc)) return rc;
3165
3166 setModified(IsModified_MachineData);
3167 mUserData.backup();
3168 mUserData->s.uFaultTolerancePort = aPort;
3169 return S_OK;
3170}
3171
3172STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword)
3173{
3174 CheckComArgOutPointerValid(aPassword);
3175
3176 AutoCaller autoCaller(this);
3177 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3178
3179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3180
3181 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword);
3182
3183 return S_OK;
3184}
3185
3186STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword)
3187{
3188 AutoCaller autoCaller(this);
3189 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3190
3191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3192
3193 /* @todo deal with running state change. */
3194 HRESULT rc = checkStateDependency(MutableStateDep);
3195 if (FAILED(rc)) return rc;
3196
3197 setModified(IsModified_MachineData);
3198 mUserData.backup();
3199 mUserData->s.strFaultTolerancePassword = aPassword;
3200
3201 return S_OK;
3202}
3203
3204STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval)
3205{
3206 CheckComArgOutPointerValid(aInterval);
3207
3208 AutoCaller autoCaller(this);
3209 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3210
3211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3212
3213 *aInterval = mUserData->s.uFaultToleranceInterval;
3214 return S_OK;
3215}
3216
3217STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval)
3218{
3219 AutoCaller autoCaller(this);
3220 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3221
3222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3223
3224 /* @todo deal with running state change. */
3225 HRESULT rc = checkStateDependency(MutableStateDep);
3226 if (FAILED(rc)) return rc;
3227
3228 setModified(IsModified_MachineData);
3229 mUserData.backup();
3230 mUserData->s.uFaultToleranceInterval = aInterval;
3231 return S_OK;
3232}
3233
3234STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled)
3235{
3236 CheckComArgOutPointerValid(aEnabled);
3237
3238 AutoCaller autoCaller(this);
3239 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3240
3241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243 *aEnabled = mUserData->s.fRTCUseUTC;
3244
3245 return S_OK;
3246}
3247
3248STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled)
3249{
3250 AutoCaller autoCaller(this);
3251 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3252
3253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 /* Only allow it to be set to true when PoweredOff or Aborted.
3256 (Clearing it is always permitted.) */
3257 if ( aEnabled
3258 && mData->mRegistered
3259 && ( !isSessionMachine()
3260 || ( mData->mMachineState != MachineState_PoweredOff
3261 && mData->mMachineState != MachineState_Teleported
3262 && mData->mMachineState != MachineState_Aborted
3263 )
3264 )
3265 )
3266 return setError(VBOX_E_INVALID_VM_STATE,
3267 tr("The machine is not powered off (state is %s)"),
3268 Global::stringifyMachineState(mData->mMachineState));
3269
3270 setModified(IsModified_MachineData);
3271 mUserData.backup();
3272 mUserData->s.fRTCUseUTC = !!aEnabled;
3273
3274 return S_OK;
3275}
3276
3277STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled)
3278{
3279 CheckComArgOutPointerValid(aEnabled);
3280
3281 AutoCaller autoCaller(this);
3282 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3283
3284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
3286 *aEnabled = mHWData->mIOCacheEnabled;
3287
3288 return S_OK;
3289}
3290
3291STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled)
3292{
3293 AutoCaller autoCaller(this);
3294 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3295
3296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3297
3298 HRESULT rc = checkStateDependency(MutableStateDep);
3299 if (FAILED(rc)) return rc;
3300
3301 setModified(IsModified_MachineData);
3302 mHWData.backup();
3303 mHWData->mIOCacheEnabled = aEnabled;
3304
3305 return S_OK;
3306}
3307
3308STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize)
3309{
3310 CheckComArgOutPointerValid(aIOCacheSize);
3311
3312 AutoCaller autoCaller(this);
3313 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3314
3315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3316
3317 *aIOCacheSize = mHWData->mIOCacheSize;
3318
3319 return S_OK;
3320}
3321
3322STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize)
3323{
3324 AutoCaller autoCaller(this);
3325 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3326
3327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3328
3329 HRESULT rc = checkStateDependency(MutableStateDep);
3330 if (FAILED(rc)) return rc;
3331
3332 setModified(IsModified_MachineData);
3333 mHWData.backup();
3334 mHWData->mIOCacheSize = aIOCacheSize;
3335
3336 return S_OK;
3337}
3338
3339
3340/**
3341 * @note Locks objects!
3342 */
3343STDMETHODIMP Machine::LockMachine(ISession *aSession,
3344 LockType_T lockType)
3345{
3346 CheckComArgNotNull(aSession);
3347
3348 AutoCaller autoCaller(this);
3349 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3350
3351 /* check the session state */
3352 SessionState_T state;
3353 HRESULT rc = aSession->COMGETTER(State)(&state);
3354 if (FAILED(rc)) return rc;
3355
3356 if (state != SessionState_Unlocked)
3357 return setError(VBOX_E_INVALID_OBJECT_STATE,
3358 tr("The given session is busy"));
3359
3360 // get the client's IInternalSessionControl interface
3361 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3362 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3363 E_INVALIDARG);
3364
3365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3366
3367 if (!mData->mRegistered)
3368 return setError(E_UNEXPECTED,
3369 tr("The machine '%s' is not registered"),
3370 mUserData->s.strName.c_str());
3371
3372 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3373
3374 SessionState_T oldState = mData->mSession.mState;
3375 /* Hack: in case the session is closing and there is a progress object
3376 * which allows waiting for the session to be closed, take the opportunity
3377 * and do a limited wait (max. 1 second). This helps a lot when the system
3378 * is busy and thus session closing can take a little while. */
3379 if ( mData->mSession.mState == SessionState_Unlocking
3380 && mData->mSession.mProgress)
3381 {
3382 alock.release();
3383 mData->mSession.mProgress->WaitForCompletion(1000);
3384 alock.acquire();
3385 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3386 }
3387
3388 // try again now
3389 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists)
3390 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock:
3391 )
3392 {
3393 // OK, share the session... we are now dealing with three processes:
3394 // 1) VBoxSVC (where this code runs);
3395 // 2) process C: the caller's client process (who wants a shared session);
3396 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3397
3398 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3399 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3400 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3401 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3402 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3403
3404 /*
3405 * Release the lock before calling the client process. It's safe here
3406 * since the only thing to do after we get the lock again is to add
3407 * the remote control to the list (which doesn't directly influence
3408 * anything).
3409 */
3410 alock.release();
3411
3412 // get the console of the session holding the write lock (this is a remote call)
3413 ComPtr<IConsole> pConsoleW;
3414 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3415 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam());
3416 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3417 if (FAILED(rc))
3418 // the failure may occur w/o any error info (from RPC), so provide one
3419 return setError(VBOX_E_VM_ERROR,
3420 tr("Failed to get a console object from the direct session (%Rrc)"), rc);
3421
3422 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3423
3424 // share the session machine and W's console with the caller's session
3425 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3426 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3427 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3428
3429 if (FAILED(rc))
3430 // the failure may occur w/o any error info (from RPC), so provide one
3431 return setError(VBOX_E_VM_ERROR,
3432 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3433 alock.acquire();
3434
3435 // need to revalidate the state after acquiring the lock again
3436 if (mData->mSession.mState != SessionState_Locked)
3437 {
3438 pSessionControl->Uninitialize();
3439 return setError(VBOX_E_INVALID_SESSION_STATE,
3440 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3441 mUserData->s.strName.c_str());
3442 }
3443
3444 // add the caller's session to the list
3445 mData->mSession.mRemoteControls.push_back(pSessionControl);
3446 }
3447 else if ( mData->mSession.mState == SessionState_Locked
3448 || mData->mSession.mState == SessionState_Unlocking
3449 )
3450 {
3451 // sharing not permitted, or machine still unlocking:
3452 return setError(VBOX_E_INVALID_OBJECT_STATE,
3453 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3454 mUserData->s.strName.c_str());
3455 }
3456 else
3457 {
3458 // machine is not locked: then write-lock the machine (create the session machine)
3459
3460 // must not be busy
3461 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3462
3463 // get the caller's session PID
3464 RTPROCESS pid = NIL_RTPROCESS;
3465 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3466 pSessionControl->GetPID((ULONG*)&pid);
3467 Assert(pid != NIL_RTPROCESS);
3468
3469 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3470
3471 if (fLaunchingVMProcess)
3472 {
3473 // this machine is awaiting for a spawning session to be opened:
3474 // then the calling process must be the one that got started by
3475 // LaunchVMProcess()
3476
3477 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3478 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3479
3480 if (mData->mSession.mPID != pid)
3481 return setError(E_ACCESSDENIED,
3482 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3483 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3484 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3485 }
3486
3487 // create the mutable SessionMachine from the current machine
3488 ComObjPtr<SessionMachine> sessionMachine;
3489 sessionMachine.createObject();
3490 rc = sessionMachine->init(this);
3491 AssertComRC(rc);
3492
3493 /* NOTE: doing return from this function after this point but
3494 * before the end is forbidden since it may call SessionMachine::uninit()
3495 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3496 * lock while still holding the Machine lock in alock so that a deadlock
3497 * is possible due to the wrong lock order. */
3498
3499 if (SUCCEEDED(rc))
3500 {
3501 /*
3502 * Set the session state to Spawning to protect against subsequent
3503 * attempts to open a session and to unregister the machine after
3504 * we release the lock.
3505 */
3506 SessionState_T origState = mData->mSession.mState;
3507 mData->mSession.mState = SessionState_Spawning;
3508
3509 /*
3510 * Release the lock before calling the client process -- it will call
3511 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3512 * because the state is Spawning, so that LaunchVMProcess() and
3513 * LockMachine() calls will fail. This method, called before we
3514 * acquire the lock again, will fail because of the wrong PID.
3515 *
3516 * Note that mData->mSession.mRemoteControls accessed outside
3517 * the lock may not be modified when state is Spawning, so it's safe.
3518 */
3519 alock.release();
3520
3521 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3522 rc = pSessionControl->AssignMachine(sessionMachine, lockType);
3523 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3524
3525 /* The failure may occur w/o any error info (from RPC), so provide one */
3526 if (FAILED(rc))
3527 setError(VBOX_E_VM_ERROR,
3528 tr("Failed to assign the machine to the session (%Rrc)"), rc);
3529
3530 if ( SUCCEEDED(rc)
3531 && fLaunchingVMProcess
3532 )
3533 {
3534 /* complete the remote session initialization */
3535
3536 /* get the console from the direct session */
3537 ComPtr<IConsole> console;
3538 rc = pSessionControl->GetRemoteConsole(console.asOutParam());
3539 ComAssertComRC(rc);
3540
3541 if (SUCCEEDED(rc) && !console)
3542 {
3543 ComAssert(!!console);
3544 rc = E_FAIL;
3545 }
3546
3547 /* assign machine & console to the remote session */
3548 if (SUCCEEDED(rc))
3549 {
3550 /*
3551 * after LaunchVMProcess(), the first and the only
3552 * entry in remoteControls is that remote session
3553 */
3554 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3555 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3556 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3557
3558 /* The failure may occur w/o any error info (from RPC), so provide one */
3559 if (FAILED(rc))
3560 setError(VBOX_E_VM_ERROR,
3561 tr("Failed to assign the machine to the remote session (%Rrc)"), rc);
3562 }
3563
3564 if (FAILED(rc))
3565 pSessionControl->Uninitialize();
3566 }
3567
3568 /* acquire the lock again */
3569 alock.acquire();
3570
3571 /* Restore the session state */
3572 mData->mSession.mState = origState;
3573 }
3574
3575 // finalize spawning anyway (this is why we don't return on errors above)
3576 if (fLaunchingVMProcess)
3577 {
3578 /* Note that the progress object is finalized later */
3579 /** @todo Consider checking mData->mSession.mProgress for cancellation
3580 * around here. */
3581
3582 /* We don't reset mSession.mPID here because it is necessary for
3583 * SessionMachine::uninit() to reap the child process later. */
3584
3585 if (FAILED(rc))
3586 {
3587 /* Close the remote session, remove the remote control from the list
3588 * and reset session state to Closed (@note keep the code in sync
3589 * with the relevant part in openSession()). */
3590
3591 Assert(mData->mSession.mRemoteControls.size() == 1);
3592 if (mData->mSession.mRemoteControls.size() == 1)
3593 {
3594 ErrorInfoKeeper eik;
3595 mData->mSession.mRemoteControls.front()->Uninitialize();
3596 }
3597
3598 mData->mSession.mRemoteControls.clear();
3599 mData->mSession.mState = SessionState_Unlocked;
3600 }
3601 }
3602 else
3603 {
3604 /* memorize PID of the directly opened session */
3605 if (SUCCEEDED(rc))
3606 mData->mSession.mPID = pid;
3607 }
3608
3609 if (SUCCEEDED(rc))
3610 {
3611 /* memorize the direct session control and cache IUnknown for it */
3612 mData->mSession.mDirectControl = pSessionControl;
3613 mData->mSession.mState = SessionState_Locked;
3614 /* associate the SessionMachine with this Machine */
3615 mData->mSession.mMachine = sessionMachine;
3616
3617 /* request an IUnknown pointer early from the remote party for later
3618 * identity checks (it will be internally cached within mDirectControl
3619 * at least on XPCOM) */
3620 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3621 NOREF(unk);
3622 }
3623
3624 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3625 * would break the lock order */
3626 alock.release();
3627
3628 /* uninitialize the created session machine on failure */
3629 if (FAILED(rc))
3630 sessionMachine->uninit();
3631
3632 }
3633
3634 if (SUCCEEDED(rc))
3635 {
3636 /*
3637 * tell the client watcher thread to update the set of
3638 * machines that have open sessions
3639 */
3640 mParent->updateClientWatcher();
3641
3642 if (oldState != SessionState_Locked)
3643 /* fire an event */
3644 mParent->onSessionStateChange(getId(), SessionState_Locked);
3645 }
3646
3647 return rc;
3648}
3649
3650/**
3651 * @note Locks objects!
3652 */
3653STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession,
3654 IN_BSTR aFrontend,
3655 IN_BSTR aEnvironment,
3656 IProgress **aProgress)
3657{
3658 CheckComArgStr(aFrontend);
3659 Utf8Str strFrontend(aFrontend);
3660 Utf8Str strEnvironment(aEnvironment);
3661 /* "emergencystop" doesn't need the session, so skip the checks/interface
3662 * retrieval. This code doesn't quite fit in here, but introducing a
3663 * special API method would be even more effort, and would require explicit
3664 * support by every API client. It's better to hide the feature a bit. */
3665 if (strFrontend != "emergencystop")
3666 CheckComArgNotNull(aSession);
3667 CheckComArgOutPointerValid(aProgress);
3668
3669 AutoCaller autoCaller(this);
3670 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3671
3672 HRESULT rc = S_OK;
3673 if (strFrontend.isEmpty())
3674 {
3675 Bstr bstrFrontend;
3676 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3677 if (FAILED(rc))
3678 return rc;
3679 strFrontend = bstrFrontend;
3680 if (strFrontend.isEmpty())
3681 {
3682 ComPtr<ISystemProperties> systemProperties;
3683 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3684 if (FAILED(rc))
3685 return rc;
3686 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3687 if (FAILED(rc))
3688 return rc;
3689 strFrontend = bstrFrontend;
3690 }
3691 /* paranoia - emergencystop is not a valid default */
3692 if (strFrontend == "emergencystop")
3693 strFrontend = Utf8Str::Empty;
3694 }
3695
3696 if (strFrontend != "emergencystop")
3697 {
3698 /* check the session state */
3699 SessionState_T state;
3700 rc = aSession->COMGETTER(State)(&state);
3701 if (FAILED(rc))
3702 return rc;
3703
3704 if (state != SessionState_Unlocked)
3705 return setError(VBOX_E_INVALID_OBJECT_STATE,
3706 tr("The given session is busy"));
3707
3708 /* get the IInternalSessionControl interface */
3709 ComPtr<IInternalSessionControl> control(aSession);
3710 ComAssertMsgRet(!control.isNull(),
3711 ("No IInternalSessionControl interface"),
3712 E_INVALIDARG);
3713
3714 /* get the teleporter enable state for the progress object init. */
3715 BOOL fTeleporterEnabled;
3716 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3717 if (FAILED(rc))
3718 return rc;
3719
3720 /* create a progress object */
3721 ComObjPtr<ProgressProxy> progress;
3722 progress.createObject();
3723 rc = progress->init(mParent,
3724 static_cast<IMachine*>(this),
3725 Bstr(tr("Starting VM")).raw(),
3726 TRUE /* aCancelable */,
3727 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3728 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3729 2 /* uFirstOperationWeight */,
3730 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3731
3732 if (SUCCEEDED(rc))
3733 {
3734 rc = launchVMProcess(control, strFrontend, strEnvironment, progress);
3735 if (SUCCEEDED(rc))
3736 {
3737 progress.queryInterfaceTo(aProgress);
3738
3739 /* signal the client watcher thread */
3740 mParent->updateClientWatcher();
3741
3742 /* fire an event */
3743 mParent->onSessionStateChange(getId(), SessionState_Spawning);
3744 }
3745 }
3746 }
3747 else
3748 {
3749 /* no progress object - either instant success or failure */
3750 *aProgress = NULL;
3751
3752 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3753
3754 if (mData->mSession.mState != SessionState_Locked)
3755 return setError(VBOX_E_INVALID_OBJECT_STATE,
3756 tr("The machine '%s' is not locked by a session"),
3757 mUserData->s.strName.c_str());
3758
3759 /* must have a VM process associated - do not kill normal API clients
3760 * with an open session */
3761 if (!Global::IsOnline(mData->mMachineState))
3762 return setError(VBOX_E_INVALID_OBJECT_STATE,
3763 tr("The machine '%s' does not have a VM process"),
3764 mUserData->s.strName.c_str());
3765
3766 /* forcibly terminate the VM process */
3767 if (mData->mSession.mPID != NIL_RTPROCESS)
3768 RTProcTerminate(mData->mSession.mPID);
3769
3770 /* signal the client watcher thread, as most likely the client has
3771 * been terminated */
3772 mParent->updateClientWatcher();
3773 }
3774
3775 return rc;
3776}
3777
3778STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice)
3779{
3780 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3781 return setError(E_INVALIDARG,
3782 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3783 aPosition, SchemaDefs::MaxBootPosition);
3784
3785 if (aDevice == DeviceType_USB)
3786 return setError(E_NOTIMPL,
3787 tr("Booting from USB device is currently not supported"));
3788
3789 AutoCaller autoCaller(this);
3790 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3791
3792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3793
3794 HRESULT rc = checkStateDependency(MutableStateDep);
3795 if (FAILED(rc)) return rc;
3796
3797 setModified(IsModified_MachineData);
3798 mHWData.backup();
3799 mHWData->mBootOrder[aPosition - 1] = aDevice;
3800
3801 return S_OK;
3802}
3803
3804STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3805{
3806 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3807 return setError(E_INVALIDARG,
3808 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3809 aPosition, SchemaDefs::MaxBootPosition);
3810
3811 AutoCaller autoCaller(this);
3812 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3813
3814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3815
3816 *aDevice = mHWData->mBootOrder[aPosition - 1];
3817
3818 return S_OK;
3819}
3820
3821STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
3822 LONG aControllerPort,
3823 LONG aDevice,
3824 DeviceType_T aType,
3825 IMedium *aMedium)
3826{
3827 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3828 aControllerName, aControllerPort, aDevice, aType, aMedium));
3829
3830 CheckComArgStrNotEmptyOrNull(aControllerName);
3831
3832 AutoCaller autoCaller(this);
3833 if (FAILED(autoCaller.rc())) return autoCaller.rc();
3834
3835 // request the host lock first, since might be calling Host methods for getting host drives;
3836 // next, protect the media tree all the while we're in here, as well as our member variables
3837 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS);
3838 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3839
3840 HRESULT rc = checkStateDependency(MutableStateDep);
3841 if (FAILED(rc)) return rc;
3842
3843 /// @todo NEWMEDIA implicit machine registration
3844 if (!mData->mRegistered)
3845 return setError(VBOX_E_INVALID_OBJECT_STATE,
3846 tr("Cannot attach storage devices to an unregistered machine"));
3847
3848 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3849
3850 /* Check for an existing controller. */
3851 ComObjPtr<StorageController> ctl;
3852 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
3853 if (FAILED(rc)) return rc;
3854
3855 StorageControllerType_T ctrlType;
3856 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3857 if (FAILED(rc))
3858 return setError(E_FAIL,
3859 tr("Could not get type of controller '%ls'"),
3860 aControllerName);
3861
3862 bool fSilent = false;
3863 Utf8Str strReconfig;
3864
3865 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3866 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3867 if (FAILED(rc))
3868 return rc;
3869 if ( mData->mMachineState == MachineState_Paused
3870 && strReconfig == "1")
3871 fSilent = true;
3872
3873 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3874 bool fHotplug = false;
3875 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3876 fHotplug = true;
3877
3878 if (fHotplug && !isControllerHotplugCapable(ctrlType))
3879 return setError(VBOX_E_INVALID_VM_STATE,
3880 tr("Controller '%ls' does not support hotplugging"),
3881 aControllerName);
3882
3883 // check that the port and device are not out of range
3884 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
3885 if (FAILED(rc)) return rc;
3886
3887 /* check if the device slot is already busy */
3888 MediumAttachment *pAttachTemp;
3889 if ((pAttachTemp = findAttachment(mMediaData->mAttachments,
3890 aControllerName,
3891 aControllerPort,
3892 aDevice)))
3893 {
3894 Medium *pMedium = pAttachTemp->getMedium();
3895 if (pMedium)
3896 {
3897 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3898 return setError(VBOX_E_OBJECT_IN_USE,
3899 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3900 pMedium->getLocationFull().c_str(),
3901 aControllerPort,
3902 aDevice,
3903 aControllerName);
3904 }
3905 else
3906 return setError(VBOX_E_OBJECT_IN_USE,
3907 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"),
3908 aControllerPort, aDevice, aControllerName);
3909 }
3910
3911 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium);
3912 if (aMedium && medium.isNull())
3913 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3914
3915 AutoCaller mediumCaller(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917
3918 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3919
3920 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium))
3921 && !medium.isNull()
3922 )
3923 return setError(VBOX_E_OBJECT_IN_USE,
3924 tr("Medium '%s' is already attached to this virtual machine"),
3925 medium->getLocationFull().c_str());
3926
3927 if (!medium.isNull())
3928 {
3929 MediumType_T mtype = medium->getType();
3930 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3931 // For DVDs it's not written to the config file, so needs no global config
3932 // version bump. For floppies it's a new attribute "type", which is ignored
3933 // by older VirtualBox version, so needs no global config version bump either.
3934 // For hard disks this type is not accepted.
3935 if (mtype == MediumType_MultiAttach)
3936 {
3937 // This type is new with VirtualBox 4.0 and therefore requires settings
3938 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3939 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3940 // two reasons: The medium type is a property of the media registry tree, which
3941 // can reside in the global config file (for pre-4.0 media); we would therefore
3942 // possibly need to bump the global config version. We don't want to do that though
3943 // because that might make downgrading to pre-4.0 impossible.
3944 // As a result, we can only use these two new types if the medium is NOT in the
3945 // global registry:
3946 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId();
3947 if ( medium->isInRegistry(uuidGlobalRegistry)
3948 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3949 )
3950 return setError(VBOX_E_INVALID_OBJECT_STATE,
3951 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3952 "to machines that were created with VirtualBox 4.0 or later"),
3953 medium->getLocationFull().c_str());
3954 }
3955 }
3956
3957 bool fIndirect = false;
3958 if (!medium.isNull())
3959 fIndirect = medium->isReadOnly();
3960 bool associate = true;
3961
3962 do
3963 {
3964 if ( aType == DeviceType_HardDisk
3965 && mMediaData.isBackedUp())
3966 {
3967 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3968
3969 /* check if the medium was attached to the VM before we started
3970 * changing attachments in which case the attachment just needs to
3971 * be restored */
3972 if ((pAttachTemp = findAttachment(oldAtts, medium)))
3973 {
3974 AssertReturn(!fIndirect, E_FAIL);
3975
3976 /* see if it's the same bus/channel/device */
3977 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice))
3978 {
3979 /* the simplest case: restore the whole attachment
3980 * and return, nothing else to do */
3981 mMediaData->mAttachments.push_back(pAttachTemp);
3982 return S_OK;
3983 }
3984
3985 /* bus/channel/device differ; we need a new attachment object,
3986 * but don't try to associate it again */
3987 associate = false;
3988 break;
3989 }
3990 }
3991
3992 /* go further only if the attachment is to be indirect */
3993 if (!fIndirect)
3994 break;
3995
3996 /* perform the so called smart attachment logic for indirect
3997 * attachments. Note that smart attachment is only applicable to base
3998 * hard disks. */
3999
4000 if (medium->getParent().isNull())
4001 {
4002 /* first, investigate the backup copy of the current hard disk
4003 * attachments to make it possible to re-attach existing diffs to
4004 * another device slot w/o losing their contents */
4005 if (mMediaData.isBackedUp())
4006 {
4007 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
4008
4009 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
4010 uint32_t foundLevel = 0;
4011
4012 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
4013 it != oldAtts.end();
4014 ++it)
4015 {
4016 uint32_t level = 0;
4017 MediumAttachment *pAttach = *it;
4018 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4019 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4020 if (pMedium.isNull())
4021 continue;
4022
4023 if (pMedium->getBase(&level) == medium)
4024 {
4025 /* skip the hard disk if its currently attached (we
4026 * cannot attach the same hard disk twice) */
4027 if (findAttachment(mMediaData->mAttachments,
4028 pMedium))
4029 continue;
4030
4031 /* matched device, channel and bus (i.e. attached to the
4032 * same place) will win and immediately stop the search;
4033 * otherwise the attachment that has the youngest
4034 * descendant of medium will be used
4035 */
4036 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
4037 {
4038 /* the simplest case: restore the whole attachment
4039 * and return, nothing else to do */
4040 mMediaData->mAttachments.push_back(*it);
4041 return S_OK;
4042 }
4043 else if ( foundIt == oldAtts.end()
4044 || level > foundLevel /* prefer younger */
4045 )
4046 {
4047 foundIt = it;
4048 foundLevel = level;
4049 }
4050 }
4051 }
4052
4053 if (foundIt != oldAtts.end())
4054 {
4055 /* use the previously attached hard disk */
4056 medium = (*foundIt)->getMedium();
4057 mediumCaller.attach(medium);
4058 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4059 mediumLock.attach(medium);
4060 /* not implicit, doesn't require association with this VM */
4061 fIndirect = false;
4062 associate = false;
4063 /* go right to the MediumAttachment creation */
4064 break;
4065 }
4066 }
4067
4068 /* must give up the medium lock and medium tree lock as below we
4069 * go over snapshots, which needs a lock with higher lock order. */
4070 mediumLock.release();
4071 treeLock.release();
4072
4073 /* then, search through snapshots for the best diff in the given
4074 * hard disk's chain to base the new diff on */
4075
4076 ComObjPtr<Medium> base;
4077 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4078 while (snap)
4079 {
4080 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4081
4082 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments;
4083
4084 MediumAttachment *pAttachFound = NULL;
4085 uint32_t foundLevel = 0;
4086
4087 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin();
4088 it != snapAtts.end();
4089 ++it)
4090 {
4091 MediumAttachment *pAttach = *it;
4092 ComObjPtr<Medium> pMedium = pAttach->getMedium();
4093 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk);
4094 if (pMedium.isNull())
4095 continue;
4096
4097 uint32_t level = 0;
4098 if (pMedium->getBase(&level) == medium)
4099 {
4100 /* matched device, channel and bus (i.e. attached to the
4101 * same place) will win and immediately stop the search;
4102 * otherwise the attachment that has the youngest
4103 * descendant of medium will be used
4104 */
4105 if ( pAttach->getDevice() == aDevice
4106 && pAttach->getPort() == aControllerPort
4107 && pAttach->getControllerName() == aControllerName
4108 )
4109 {
4110 pAttachFound = pAttach;
4111 break;
4112 }
4113 else if ( !pAttachFound
4114 || level > foundLevel /* prefer younger */
4115 )
4116 {
4117 pAttachFound = pAttach;
4118 foundLevel = level;
4119 }
4120 }
4121 }
4122
4123 if (pAttachFound)
4124 {
4125 base = pAttachFound->getMedium();
4126 break;
4127 }
4128
4129 snap = snap->getParent();
4130 }
4131
4132 /* re-lock medium tree and the medium, as we need it below */
4133 treeLock.acquire();
4134 mediumLock.acquire();
4135
4136 /* found a suitable diff, use it as a base */
4137 if (!base.isNull())
4138 {
4139 medium = base;
4140 mediumCaller.attach(medium);
4141 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4142 mediumLock.attach(medium);
4143 }
4144 }
4145
4146 Utf8Str strFullSnapshotFolder;
4147 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4148
4149 ComObjPtr<Medium> diff;
4150 diff.createObject();
4151 // store this diff in the same registry as the parent
4152 Guid uuidRegistryParent;
4153 if (!medium->getFirstRegistryMachineId(uuidRegistryParent))
4154 {
4155 // parent image has no registry: this can happen if we're attaching a new immutable
4156 // image that has not yet been attached (medium then points to the base and we're
4157 // creating the diff image for the immutable, and the parent is not yet registered);
4158 // put the parent in the machine registry then
4159 mediumLock.release();
4160 treeLock.release();
4161 alock.release();
4162 addMediumToRegistry(medium);
4163 alock.acquire();
4164 treeLock.acquire();
4165 mediumLock.acquire();
4166 medium->getFirstRegistryMachineId(uuidRegistryParent);
4167 }
4168 rc = diff->init(mParent,
4169 medium->getPreferredDiffFormat(),
4170 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4171 uuidRegistryParent);
4172 if (FAILED(rc)) return rc;
4173
4174 /* Apply the normal locking logic to the entire chain. */
4175 MediumLockList *pMediumLockList(new MediumLockList());
4176 mediumLock.release();
4177 treeLock.release();
4178 rc = diff->createMediumLockList(true /* fFailIfInaccessible */,
4179 true /* fMediumLockWrite */,
4180 medium,
4181 *pMediumLockList);
4182 treeLock.acquire();
4183 mediumLock.acquire();
4184 if (SUCCEEDED(rc))
4185 {
4186 mediumLock.release();
4187 treeLock.release();
4188 rc = pMediumLockList->Lock();
4189 treeLock.acquire();
4190 mediumLock.acquire();
4191 if (FAILED(rc))
4192 setError(rc,
4193 tr("Could not lock medium when creating diff '%s'"),
4194 diff->getLocationFull().c_str());
4195 else
4196 {
4197 /* will release the lock before the potentially lengthy
4198 * operation, so protect with the special state */
4199 MachineState_T oldState = mData->mMachineState;
4200 setMachineState(MachineState_SettingUp);
4201
4202 mediumLock.release();
4203 treeLock.release();
4204 alock.release();
4205
4206 rc = medium->createDiffStorage(diff,
4207 MediumVariant_Standard,
4208 pMediumLockList,
4209 NULL /* aProgress */,
4210 true /* aWait */);
4211
4212 alock.acquire();
4213 treeLock.acquire();
4214 mediumLock.acquire();
4215
4216 setMachineState(oldState);
4217 }
4218 }
4219
4220 /* Unlock the media and free the associated memory. */
4221 delete pMediumLockList;
4222
4223 if (FAILED(rc)) return rc;
4224
4225 /* use the created diff for the actual attachment */
4226 medium = diff;
4227 mediumCaller.attach(medium);
4228 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4229 mediumLock.attach(medium);
4230 }
4231 while (0);
4232
4233 ComObjPtr<MediumAttachment> attachment;
4234 attachment.createObject();
4235 rc = attachment->init(this,
4236 medium,
4237 aControllerName,
4238 aControllerPort,
4239 aDevice,
4240 aType,
4241 fIndirect,
4242 false /* fPassthrough */,
4243 false /* fTempEject */,
4244 false /* fNonRotational */,
4245 false /* fDiscard */,
4246 Utf8Str::Empty);
4247 if (FAILED(rc)) return rc;
4248
4249 if (associate && !medium.isNull())
4250 {
4251 // as the last step, associate the medium to the VM
4252 rc = medium->addBackReference(mData->mUuid);
4253 // here we can fail because of Deleting, or being in process of creating a Diff
4254 if (FAILED(rc)) return rc;
4255
4256 mediumLock.release();
4257 treeLock.release();
4258 alock.release();
4259 addMediumToRegistry(medium);
4260 alock.acquire();
4261 treeLock.acquire();
4262 mediumLock.acquire();
4263 }
4264
4265 /* success: finally remember the attachment */
4266 setModified(IsModified_Storage);
4267 mMediaData.backup();
4268 mMediaData->mAttachments.push_back(attachment);
4269
4270 mediumLock.release();
4271 treeLock.release();
4272 alock.release();
4273
4274 if (fHotplug || fSilent)
4275 {
4276 MediumLockList *pMediumLockList(new MediumLockList());
4277
4278 rc = medium->createMediumLockList(true /* fFailIfInaccessible */,
4279 true /* fMediumLockWrite */,
4280 NULL,
4281 *pMediumLockList);
4282 alock.acquire();
4283 if (FAILED(rc))
4284 delete pMediumLockList;
4285 else
4286 {
4287 mData->mSession.mLockedMedia.Unlock();
4288 alock.release();
4289 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4290 mData->mSession.mLockedMedia.Lock();
4291 alock.acquire();
4292 }
4293 alock.release();
4294
4295 if (SUCCEEDED(rc))
4296 {
4297 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4298 /* Remove lock list in case of error. */
4299 if (FAILED(rc))
4300 {
4301 mData->mSession.mLockedMedia.Unlock();
4302 mData->mSession.mLockedMedia.Remove(attachment);
4303 mData->mSession.mLockedMedia.Lock();
4304 }
4305 }
4306 }
4307
4308 mParent->saveModifiedRegistries();
4309
4310 return rc;
4311}
4312
4313STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort,
4314 LONG aDevice)
4315{
4316 CheckComArgStrNotEmptyOrNull(aControllerName);
4317
4318 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4319 aControllerName, aControllerPort, aDevice));
4320
4321 AutoCaller autoCaller(this);
4322 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4323
4324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4325
4326 HRESULT rc = checkStateDependency(MutableStateDep);
4327 if (FAILED(rc)) return rc;
4328
4329 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4330
4331 /* Check for an existing controller. */
4332 ComObjPtr<StorageController> ctl;
4333 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
4334 if (FAILED(rc)) return rc;
4335
4336 StorageControllerType_T ctrlType;
4337 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4338 if (FAILED(rc))
4339 return setError(E_FAIL,
4340 tr("Could not get type of controller '%ls'"),
4341 aControllerName);
4342
4343 bool fSilent = false;
4344 Utf8Str strReconfig;
4345
4346 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4347 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4348 if (FAILED(rc))
4349 return rc;
4350 if ( mData->mMachineState == MachineState_Paused
4351 && strReconfig == "1")
4352 fSilent = true;
4353
4354 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4355 bool fHotplug = false;
4356 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4357 fHotplug = true;
4358
4359 if (fHotplug && !isControllerHotplugCapable(ctrlType))
4360 return setError(VBOX_E_INVALID_VM_STATE,
4361 tr("Controller '%ls' does not support hotplugging"),
4362 aControllerName);
4363
4364 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4365 aControllerName,
4366 aControllerPort,
4367 aDevice);
4368 if (!pAttach)
4369 return setError(VBOX_E_OBJECT_NOT_FOUND,
4370 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4371 aDevice, aControllerPort, aControllerName);
4372
4373 /*
4374 * The VM has to detach the device before we delete any implicit diffs.
4375 * If this fails we can roll back without loosing data.
4376 */
4377 if (fHotplug || fSilent)
4378 {
4379 alock.release();
4380 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4381 alock.acquire();
4382 }
4383 if (FAILED(rc)) return rc;
4384
4385 /* If we are here everything went well and we can delete the implicit now. */
4386 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */);
4387
4388 alock.release();
4389
4390 mParent->saveModifiedRegistries();
4391
4392 return rc;
4393}
4394
4395STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort,
4396 LONG aDevice, BOOL aPassthrough)
4397{
4398 CheckComArgStrNotEmptyOrNull(aControllerName);
4399
4400 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4401 aControllerName, aControllerPort, aDevice, aPassthrough));
4402
4403 AutoCaller autoCaller(this);
4404 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4405
4406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4407
4408 HRESULT rc = checkStateDependency(MutableStateDep);
4409 if (FAILED(rc)) return rc;
4410
4411 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4412
4413 if (Global::IsOnlineOrTransient(mData->mMachineState))
4414 return setError(VBOX_E_INVALID_VM_STATE,
4415 tr("Invalid machine state: %s"),
4416 Global::stringifyMachineState(mData->mMachineState));
4417
4418 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4419 aControllerName,
4420 aControllerPort,
4421 aDevice);
4422 if (!pAttach)
4423 return setError(VBOX_E_OBJECT_NOT_FOUND,
4424 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4425 aDevice, aControllerPort, aControllerName);
4426
4427
4428 setModified(IsModified_Storage);
4429 mMediaData.backup();
4430
4431 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4432
4433 if (pAttach->getType() != DeviceType_DVD)
4434 return setError(E_INVALIDARG,
4435 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4436 aDevice, aControllerPort, aControllerName);
4437 pAttach->updatePassthrough(!!aPassthrough);
4438
4439 return S_OK;
4440}
4441
4442STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
4443 LONG aDevice, BOOL aTemporaryEject)
4444{
4445 CheckComArgStrNotEmptyOrNull(aControllerName);
4446
4447 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4448 aControllerName, aControllerPort, aDevice, aTemporaryEject));
4449
4450 AutoCaller autoCaller(this);
4451 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4452
4453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4454
4455 HRESULT rc = checkStateDependency(MutableStateDep);
4456 if (FAILED(rc)) return rc;
4457
4458 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4459 aControllerName,
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4465 aDevice, aControllerPort, aControllerName);
4466
4467
4468 setModified(IsModified_Storage);
4469 mMediaData.backup();
4470
4471 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4472
4473 if (pAttach->getType() != DeviceType_DVD)
4474 return setError(E_INVALIDARG,
4475 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
4476 aDevice, aControllerPort, aControllerName);
4477 pAttach->updateTempEject(!!aTemporaryEject);
4478
4479 return S_OK;
4480}
4481
4482STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
4483 LONG aDevice, BOOL aNonRotational)
4484{
4485 CheckComArgStrNotEmptyOrNull(aControllerName);
4486
4487 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4488 aControllerName, aControllerPort, aDevice, aNonRotational));
4489
4490 AutoCaller autoCaller(this);
4491 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4492
4493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4494
4495 HRESULT rc = checkStateDependency(MutableStateDep);
4496 if (FAILED(rc)) return rc;
4497
4498 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4499
4500 if (Global::IsOnlineOrTransient(mData->mMachineState))
4501 return setError(VBOX_E_INVALID_VM_STATE,
4502 tr("Invalid machine state: %s"),
4503 Global::stringifyMachineState(mData->mMachineState));
4504
4505 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4506 aControllerName,
4507 aControllerPort,
4508 aDevice);
4509 if (!pAttach)
4510 return setError(VBOX_E_OBJECT_NOT_FOUND,
4511 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4512 aDevice, aControllerPort, aControllerName);
4513
4514
4515 setModified(IsModified_Storage);
4516 mMediaData.backup();
4517
4518 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4519
4520 if (pAttach->getType() != DeviceType_HardDisk)
4521 return setError(E_INVALIDARG,
4522 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"),
4523 aDevice, aControllerPort, aControllerName);
4524 pAttach->updateNonRotational(!!aNonRotational);
4525
4526 return S_OK;
4527}
4528
4529STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4530 LONG aDevice, BOOL aDiscard)
4531{
4532 CheckComArgStrNotEmptyOrNull(aControllerName);
4533
4534 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4535 aControllerName, aControllerPort, aDevice, aDiscard));
4536
4537 AutoCaller autoCaller(this);
4538 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4539
4540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4541
4542 HRESULT rc = checkStateDependency(MutableStateDep);
4543 if (FAILED(rc)) return rc;
4544
4545 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4546
4547 if (Global::IsOnlineOrTransient(mData->mMachineState))
4548 return setError(VBOX_E_INVALID_VM_STATE,
4549 tr("Invalid machine state: %s"),
4550 Global::stringifyMachineState(mData->mMachineState));
4551
4552 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4553 aControllerName,
4554 aControllerPort,
4555 aDevice);
4556 if (!pAttach)
4557 return setError(VBOX_E_OBJECT_NOT_FOUND,
4558 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4559 aDevice, aControllerPort, aControllerName);
4560
4561
4562 setModified(IsModified_Storage);
4563 mMediaData.backup();
4564
4565 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4566
4567 if (pAttach->getType() != DeviceType_HardDisk)
4568 return setError(E_INVALIDARG,
4569 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"),
4570 aDevice, aControllerPort, aControllerName);
4571 pAttach->updateDiscard(!!aDiscard);
4572
4573 return S_OK;
4574}
4575
4576STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4577 LONG aDevice)
4578{
4579 int rc = S_OK;
4580 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4581 aControllerName, aControllerPort, aDevice));
4582
4583 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL);
4584
4585 return rc;
4586}
4587
4588STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
4589 LONG aDevice, IBandwidthGroup *aBandwidthGroup)
4590{
4591 CheckComArgStrNotEmptyOrNull(aControllerName);
4592
4593 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4594 aControllerName, aControllerPort, aDevice));
4595
4596 AutoCaller autoCaller(this);
4597 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4598
4599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4600
4601 HRESULT rc = checkStateDependency(MutableStateDep);
4602 if (FAILED(rc)) return rc;
4603
4604 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4605
4606 if (Global::IsOnlineOrTransient(mData->mMachineState))
4607 return setError(VBOX_E_INVALID_VM_STATE,
4608 tr("Invalid machine state: %s"),
4609 Global::stringifyMachineState(mData->mMachineState));
4610
4611 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
4612 aControllerName,
4613 aControllerPort,
4614 aDevice);
4615 if (!pAttach)
4616 return setError(VBOX_E_OBJECT_NOT_FOUND,
4617 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4618 aDevice, aControllerPort, aControllerName);
4619
4620
4621 setModified(IsModified_Storage);
4622 mMediaData.backup();
4623
4624 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup);
4625 if (aBandwidthGroup && group.isNull())
4626 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4627
4628 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4629
4630 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup();
4631 if (strBandwidthGroupOld.isNotEmpty())
4632 {
4633 /* Get the bandwidth group object and release it - this must not fail. */
4634 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4635 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4636 Assert(SUCCEEDED(rc));
4637
4638 pBandwidthGroupOld->release();
4639 pAttach->updateBandwidthGroup(Utf8Str::Empty);
4640 }
4641
4642 if (!group.isNull())
4643 {
4644 group->reference();
4645 pAttach->updateBandwidthGroup(group->getName());
4646 }
4647
4648 return S_OK;
4649}
4650
4651STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName,
4652 LONG aControllerPort,
4653 LONG aDevice,
4654 DeviceType_T aType)
4655{
4656 HRESULT rc = S_OK;
4657
4658 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4659 aControllerName, aControllerPort, aDevice, aType));
4660
4661 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL);
4662
4663 return rc;
4664}
4665
4666
4667
4668STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName,
4669 LONG aControllerPort,
4670 LONG aDevice,
4671 BOOL aForce)
4672{
4673 int rc = S_OK;
4674 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d",
4675 aControllerName, aControllerPort, aForce));
4676
4677 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce);
4678
4679 return rc;
4680}
4681
4682STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
4683 LONG aControllerPort,
4684 LONG aDevice,
4685 IMedium *aMedium,
4686 BOOL aForce)
4687{
4688 int rc = S_OK;
4689 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
4690 aControllerName, aControllerPort, aDevice, aForce));
4691
4692 CheckComArgStrNotEmptyOrNull(aControllerName);
4693
4694 AutoCaller autoCaller(this);
4695 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4696
4697 // request the host lock first, since might be calling Host methods for getting host drives;
4698 // next, protect the media tree all the while we're in here, as well as our member variables
4699 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
4700 this->lockHandle(),
4701 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4702
4703 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4704 aControllerName,
4705 aControllerPort,
4706 aDevice);
4707 if (pAttach.isNull())
4708 return setError(VBOX_E_OBJECT_NOT_FOUND,
4709 tr("No drive attached to device slot %d on port %d of controller '%ls'"),
4710 aDevice, aControllerPort, aControllerName);
4711
4712 /* Remember previously mounted medium. The medium before taking the
4713 * backup is not necessarily the same thing. */
4714 ComObjPtr<Medium> oldmedium;
4715 oldmedium = pAttach->getMedium();
4716
4717 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium);
4718 if (aMedium && pMedium.isNull())
4719 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4720
4721 AutoCaller mediumCaller(pMedium);
4722 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4723
4724 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4725 if (pMedium)
4726 {
4727 DeviceType_T mediumType = pAttach->getType();
4728 switch (mediumType)
4729 {
4730 case DeviceType_DVD:
4731 case DeviceType_Floppy:
4732 break;
4733
4734 default:
4735 return setError(VBOX_E_INVALID_OBJECT_STATE,
4736 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"),
4737 aControllerPort,
4738 aDevice,
4739 aControllerName);
4740 }
4741 }
4742
4743 setModified(IsModified_Storage);
4744 mMediaData.backup();
4745
4746 {
4747 // The backup operation makes the pAttach reference point to the
4748 // old settings. Re-get the correct reference.
4749 pAttach = findAttachment(mMediaData->mAttachments,
4750 aControllerName,
4751 aControllerPort,
4752 aDevice);
4753 if (!oldmedium.isNull())
4754 oldmedium->removeBackReference(mData->mUuid);
4755 if (!pMedium.isNull())
4756 {
4757 pMedium->addBackReference(mData->mUuid);
4758
4759 mediumLock.release();
4760 multiLock.release();
4761 addMediumToRegistry(pMedium);
4762 multiLock.acquire();
4763 mediumLock.acquire();
4764 }
4765
4766 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4767 pAttach->updateMedium(pMedium);
4768 }
4769
4770 setModified(IsModified_Storage);
4771
4772 mediumLock.release();
4773 multiLock.release();
4774 rc = onMediumChange(pAttach, aForce);
4775 multiLock.acquire();
4776 mediumLock.acquire();
4777
4778 /* On error roll back this change only. */
4779 if (FAILED(rc))
4780 {
4781 if (!pMedium.isNull())
4782 pMedium->removeBackReference(mData->mUuid);
4783 pAttach = findAttachment(mMediaData->mAttachments,
4784 aControllerName,
4785 aControllerPort,
4786 aDevice);
4787 /* If the attachment is gone in the meantime, bail out. */
4788 if (pAttach.isNull())
4789 return rc;
4790 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4791 if (!oldmedium.isNull())
4792 oldmedium->addBackReference(mData->mUuid);
4793 pAttach->updateMedium(oldmedium);
4794 }
4795
4796 mediumLock.release();
4797 multiLock.release();
4798
4799 mParent->saveModifiedRegistries();
4800
4801 return rc;
4802}
4803
4804STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
4805 LONG aControllerPort,
4806 LONG aDevice,
4807 IMedium **aMedium)
4808{
4809 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
4810 aControllerName, aControllerPort, aDevice));
4811
4812 CheckComArgStrNotEmptyOrNull(aControllerName);
4813 CheckComArgOutPointerValid(aMedium);
4814
4815 AutoCaller autoCaller(this);
4816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4817
4818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4819
4820 *aMedium = NULL;
4821
4822 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
4823 aControllerName,
4824 aControllerPort,
4825 aDevice);
4826 if (pAttach.isNull())
4827 return setError(VBOX_E_OBJECT_NOT_FOUND,
4828 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
4829 aDevice, aControllerPort, aControllerName);
4830
4831 pAttach->getMedium().queryInterfaceTo(aMedium);
4832
4833 return S_OK;
4834}
4835
4836STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port)
4837{
4838 CheckComArgOutPointerValid(port);
4839 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts));
4840
4841 AutoCaller autoCaller(this);
4842 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4843
4844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4845
4846 mSerialPorts[slot].queryInterfaceTo(port);
4847
4848 return S_OK;
4849}
4850
4851STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port)
4852{
4853 CheckComArgOutPointerValid(port);
4854 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts));
4855
4856 AutoCaller autoCaller(this);
4857 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4858
4859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4860
4861 mParallelPorts[slot].queryInterfaceTo(port);
4862
4863 return S_OK;
4864}
4865
4866STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter)
4867{
4868 CheckComArgOutPointerValid(adapter);
4869 /* Do not assert if slot is out of range, just return the advertised
4870 status. testdriver/vbox.py triggers this in logVmInfo. */
4871 if (slot >= mNetworkAdapters.size())
4872 return setError(E_INVALIDARG,
4873 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4874 slot, mNetworkAdapters.size());
4875
4876 AutoCaller autoCaller(this);
4877 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4878
4879 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4880
4881 mNetworkAdapters[slot].queryInterfaceTo(adapter);
4882
4883 return S_OK;
4884}
4885
4886STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys))
4887{
4888 CheckComArgOutSafeArrayPointerValid(aKeys);
4889
4890 AutoCaller autoCaller(this);
4891 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4892
4893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4894
4895 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size());
4896 int i = 0;
4897 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4898 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4899 ++it, ++i)
4900 {
4901 const Utf8Str &strKey = it->first;
4902 strKey.cloneTo(&saKeys[i]);
4903 }
4904 saKeys.detachTo(ComSafeArrayOutArg(aKeys));
4905
4906 return S_OK;
4907 }
4908
4909 /**
4910 * @note Locks this object for reading.
4911 */
4912STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey,
4913 BSTR *aValue)
4914{
4915 CheckComArgStrNotEmptyOrNull(aKey);
4916 CheckComArgOutPointerValid(aValue);
4917
4918 AutoCaller autoCaller(this);
4919 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4920
4921 /* start with nothing found */
4922 Bstr bstrResult("");
4923
4924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4925
4926 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey));
4927 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4928 // found:
4929 bstrResult = it->second; // source is a Utf8Str
4930
4931 /* return the result to caller (may be empty) */
4932 bstrResult.cloneTo(aValue);
4933
4934 return S_OK;
4935}
4936
4937 /**
4938 * @note Locks mParent for writing + this object for writing.
4939 */
4940STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue)
4941{
4942 CheckComArgStrNotEmptyOrNull(aKey);
4943
4944 AutoCaller autoCaller(this);
4945 if (FAILED(autoCaller.rc())) return autoCaller.rc();
4946
4947 Utf8Str strKey(aKey);
4948 Utf8Str strValue(aValue);
4949 Utf8Str strOldValue; // empty
4950
4951 // locking note: we only hold the read lock briefly to look up the old value,
4952 // then release it and call the onExtraCanChange callbacks. There is a small
4953 // chance of a race insofar as the callback might be called twice if two callers
4954 // change the same key at the same time, but that's a much better solution
4955 // than the deadlock we had here before. The actual changing of the extradata
4956 // is then performed under the write lock and race-free.
4957
4958 // look up the old value first; if nothing has changed then we need not do anything
4959 {
4960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4961 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
4962 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4963 strOldValue = it->second;
4964 }
4965
4966 bool fChanged;
4967 if ((fChanged = (strOldValue != strValue)))
4968 {
4969 // ask for permission from all listeners outside the locks;
4970 // onExtraDataCanChange() only briefly requests the VirtualBox
4971 // lock to copy the list of callbacks to invoke
4972 Bstr error;
4973 Bstr bstrValue(aValue);
4974
4975 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error))
4976 {
4977 const char *sep = error.isEmpty() ? "" : ": ";
4978 CBSTR err = error.raw();
4979 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n",
4980 sep, err));
4981 return setError(E_ACCESSDENIED,
4982 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"),
4983 aKey,
4984 bstrValue.raw(),
4985 sep,
4986 err);
4987 }
4988
4989 // data is changing and change not vetoed: then write it out under the lock
4990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4991
4992 if (isSnapshotMachine())
4993 {
4994 HRESULT rc = checkStateDependency(MutableStateDep);
4995 if (FAILED(rc)) return rc;
4996 }
4997
4998 if (strValue.isEmpty())
4999 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey);
5000 else
5001 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue;
5002 // creates a new key if needed
5003
5004 bool fNeedsGlobalSaveSettings = false;
5005 saveSettings(&fNeedsGlobalSaveSettings);
5006
5007 if (fNeedsGlobalSaveSettings)
5008 {
5009 // save the global settings; for that we should hold only the VirtualBox lock
5010 alock.release();
5011 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5012 mParent->saveSettings();
5013 }
5014 }
5015
5016 // fire notification outside the lock
5017 if (fChanged)
5018 mParent->onExtraDataChange(mData->mUuid, aKey, aValue);
5019
5020 return S_OK;
5021}
5022
5023STDMETHODIMP Machine::SaveSettings()
5024{
5025 AutoCaller autoCaller(this);
5026 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5027
5028 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5029
5030 /* when there was auto-conversion, we want to save the file even if
5031 * the VM is saved */
5032 HRESULT rc = checkStateDependency(MutableOrSavedStateDep);
5033 if (FAILED(rc)) return rc;
5034
5035 /* the settings file path may never be null */
5036 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5037
5038 /* save all VM data excluding snapshots */
5039 bool fNeedsGlobalSaveSettings = false;
5040 rc = saveSettings(&fNeedsGlobalSaveSettings);
5041 mlock.release();
5042
5043 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5044 {
5045 // save the global settings; for that we should hold only the VirtualBox lock
5046 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5047 rc = mParent->saveSettings();
5048 }
5049
5050 return rc;
5051}
5052
5053STDMETHODIMP Machine::DiscardSettings()
5054{
5055 AutoCaller autoCaller(this);
5056 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5057
5058 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5059
5060 HRESULT rc = checkStateDependency(MutableStateDep);
5061 if (FAILED(rc)) return rc;
5062
5063 /*
5064 * during this rollback, the session will be notified if data has
5065 * been actually changed
5066 */
5067 rollback(true /* aNotify */);
5068
5069 return S_OK;
5070}
5071
5072/** @note Locks objects! */
5073STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
5074 ComSafeArrayOut(IMedium*, aMedia))
5075{
5076 // use AutoLimitedCaller because this call is valid on inaccessible machines as well
5077 AutoLimitedCaller autoCaller(this);
5078 AssertComRCReturnRC(autoCaller.rc());
5079
5080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5081
5082 Guid id(getId());
5083
5084 if (mData->mSession.mState != SessionState_Unlocked)
5085 return setError(VBOX_E_INVALID_OBJECT_STATE,
5086 tr("Cannot unregister the machine '%s' while it is locked"),
5087 mUserData->s.strName.c_str());
5088
5089 // wait for state dependents to drop to zero
5090 ensureNoStateDependencies();
5091
5092 if (!mData->mAccessible)
5093 {
5094 // inaccessible maschines can only be unregistered; uninitialize ourselves
5095 // here because currently there may be no unregistered that are inaccessible
5096 // (this state combination is not supported). Note releasing the caller and
5097 // leaving the lock before calling uninit()
5098 alock.release();
5099 autoCaller.release();
5100
5101 uninit();
5102
5103 mParent->unregisterMachine(this, id);
5104 // calls VirtualBox::saveSettings()
5105
5106 return S_OK;
5107 }
5108
5109 HRESULT rc = S_OK;
5110
5111 // discard saved state
5112 if (mData->mMachineState == MachineState_Saved)
5113 {
5114 // add the saved state file to the list of files the caller should delete
5115 Assert(!mSSData->strStateFilePath.isEmpty());
5116 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5117
5118 mSSData->strStateFilePath.setNull();
5119
5120 // unconditionally set the machine state to powered off, we now
5121 // know no session has locked the machine
5122 mData->mMachineState = MachineState_PoweredOff;
5123 }
5124
5125 size_t cSnapshots = 0;
5126 if (mData->mFirstSnapshot)
5127 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1;
5128 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly)
5129 // fail now before we start detaching media
5130 return setError(VBOX_E_INVALID_OBJECT_STATE,
5131 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5132 mUserData->s.strName.c_str(), cSnapshots);
5133
5134 // This list collects the medium objects from all medium attachments
5135 // which we will detach from the machine and its snapshots, in a specific
5136 // order which allows for closing all media without getting "media in use"
5137 // errors, simply by going through the list from the front to the back:
5138 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5139 // and must be closed before the parent media from the snapshots, or closing the parents
5140 // will fail because they still have children);
5141 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5142 // the root ("first") snapshot of the machine.
5143 MediaList llMedia;
5144
5145 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5146 && mMediaData->mAttachments.size()
5147 )
5148 {
5149 // we have media attachments: detach them all and add the Medium objects to our list
5150 if (cleanupMode != CleanupMode_UnregisterOnly)
5151 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia);
5152 else
5153 return setError(VBOX_E_INVALID_OBJECT_STATE,
5154 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5155 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5156 }
5157
5158 if (cSnapshots)
5159 {
5160 // autoCleanup must be true here, or we would have failed above
5161
5162 // add the media from the medium attachments of the snapshots to llMedia
5163 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5164 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5165 // into the children first
5166
5167 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5168 MachineState_T oldState = mData->mMachineState;
5169 mData->mMachineState = MachineState_DeletingSnapshot;
5170
5171 // make a copy of the first snapshot so the refcount does not drop to 0
5172 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5173 // because of the AutoCaller voodoo)
5174 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5175
5176 // GO!
5177 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete);
5178
5179 mData->mMachineState = oldState;
5180 }
5181
5182 if (FAILED(rc))
5183 {
5184 rollbackMedia();
5185 return rc;
5186 }
5187
5188 // commit all the media changes made above
5189 commitMedia();
5190
5191 mData->mRegistered = false;
5192
5193 // machine lock no longer needed
5194 alock.release();
5195
5196 // return media to caller
5197 SafeIfaceArray<IMedium> sfaMedia(llMedia);
5198 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia));
5199
5200 mParent->unregisterMachine(this, id);
5201 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries()
5202
5203 return S_OK;
5204}
5205
5206struct Machine::DeleteTask
5207{
5208 ComObjPtr<Machine> pMachine;
5209 RTCList<ComPtr<IMedium> > llMediums;
5210 StringsList llFilesToDelete;
5211 ComObjPtr<Progress> pProgress;
5212};
5213
5214STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress)
5215{
5216 LogFlowFuncEnter();
5217
5218 AutoCaller autoCaller(this);
5219 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5220
5221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5222
5223 HRESULT rc = checkStateDependency(MutableStateDep);
5224 if (FAILED(rc)) return rc;
5225
5226 if (mData->mRegistered)
5227 return setError(VBOX_E_INVALID_VM_STATE,
5228 tr("Cannot delete settings of a registered machine"));
5229
5230 DeleteTask *pTask = new DeleteTask;
5231 pTask->pMachine = this;
5232 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia));
5233
5234 // collect files to delete
5235 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister()
5236
5237 for (size_t i = 0; i < sfaMedia.size(); ++i)
5238 {
5239 IMedium *pIMedium(sfaMedia[i]);
5240 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5241 if (pMedium.isNull())
5242 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5243 SafeArray<BSTR> ids;
5244 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5245 if (FAILED(rc)) return rc;
5246 /* At this point the medium should not have any back references
5247 * anymore. If it has it is attached to another VM and *must* not
5248 * deleted. */
5249 if (ids.size() < 1)
5250 pTask->llMediums.append(pMedium);
5251 }
5252 if (mData->pMachineConfigFile->fileExists())
5253 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
5254
5255 pTask->pProgress.createObject();
5256 pTask->pProgress->init(getVirtualBox(),
5257 static_cast<IMachine*>(this) /* aInitiator */,
5258 Bstr(tr("Deleting files")).raw(),
5259 true /* fCancellable */,
5260 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
5261 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
5262
5263 int vrc = RTThreadCreate(NULL,
5264 Machine::deleteThread,
5265 (void*)pTask,
5266 0,
5267 RTTHREADTYPE_MAIN_WORKER,
5268 0,
5269 "MachineDelete");
5270
5271 pTask->pProgress.queryInterfaceTo(aProgress);
5272
5273 if (RT_FAILURE(vrc))
5274 {
5275 delete pTask;
5276 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc);
5277 }
5278
5279 LogFlowFuncLeave();
5280
5281 return S_OK;
5282}
5283
5284/**
5285 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then
5286 * calls Machine::deleteTaskWorker() on the actual machine object.
5287 * @param Thread
5288 * @param pvUser
5289 * @return
5290 */
5291/*static*/
5292DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser)
5293{
5294 LogFlowFuncEnter();
5295
5296 DeleteTask *pTask = (DeleteTask*)pvUser;
5297 Assert(pTask);
5298 Assert(pTask->pMachine);
5299 Assert(pTask->pProgress);
5300
5301 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask);
5302 pTask->pProgress->notifyComplete(rc);
5303
5304 delete pTask;
5305
5306 LogFlowFuncLeave();
5307
5308 NOREF(Thread);
5309
5310 return VINF_SUCCESS;
5311}
5312
5313/**
5314 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread().
5315 * @param task
5316 * @return
5317 */
5318HRESULT Machine::deleteTaskWorker(DeleteTask &task)
5319{
5320 AutoCaller autoCaller(this);
5321 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5322
5323 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5324
5325 HRESULT rc = S_OK;
5326
5327 try
5328 {
5329 ULONG uLogHistoryCount = 3;
5330 ComPtr<ISystemProperties> systemProperties;
5331 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5332 if (FAILED(rc)) throw rc;
5333
5334 if (!systemProperties.isNull())
5335 {
5336 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5337 if (FAILED(rc)) throw rc;
5338 }
5339
5340 MachineState_T oldState = mData->mMachineState;
5341 setMachineState(MachineState_SettingUp);
5342 alock.release();
5343 for (size_t i = 0; i < task.llMediums.size(); ++i)
5344 {
5345 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
5346 {
5347 AutoCaller mac(pMedium);
5348 if (FAILED(mac.rc())) throw mac.rc();
5349 Utf8Str strLocation = pMedium->getLocationFull();
5350 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5351 if (FAILED(rc)) throw rc;
5352 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5353 }
5354 ComPtr<IProgress> pProgress2;
5355 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5356 if (FAILED(rc)) throw rc;
5357 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
5358 if (FAILED(rc)) throw rc;
5359 /* Check the result of the asynchrony process. */
5360 LONG iRc;
5361 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5362 if (FAILED(rc)) throw rc;
5363 /* If the thread of the progress object has an error, then
5364 * retrieve the error info from there, or it'll be lost. */
5365 if (FAILED(iRc))
5366 throw setError(ProgressErrorInfo(pProgress2));
5367 }
5368 setMachineState(oldState);
5369 alock.acquire();
5370
5371 // delete the files pushed on the task list by Machine::Delete()
5372 // (this includes saved states of the machine and snapshots and
5373 // medium storage files from the IMedium list passed in, and the
5374 // machine XML file)
5375 StringsList::const_iterator it = task.llFilesToDelete.begin();
5376 while (it != task.llFilesToDelete.end())
5377 {
5378 const Utf8Str &strFile = *it;
5379 LogFunc(("Deleting file %s\n", strFile.c_str()));
5380 int vrc = RTFileDelete(strFile.c_str());
5381 if (RT_FAILURE(vrc))
5382 throw setError(VBOX_E_IPRT_ERROR,
5383 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5384
5385 ++it;
5386 if (it == task.llFilesToDelete.end())
5387 {
5388 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5389 if (FAILED(rc)) throw rc;
5390 break;
5391 }
5392
5393 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5394 if (FAILED(rc)) throw rc;
5395 }
5396
5397 /* delete the settings only when the file actually exists */
5398 if (mData->pMachineConfigFile->fileExists())
5399 {
5400 /* Delete any backup or uncommitted XML files. Ignore failures.
5401 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5402 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5403 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5404 RTFileDelete(otherXml.c_str());
5405 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5406 RTFileDelete(otherXml.c_str());
5407
5408 /* delete the Logs folder, nothing important should be left
5409 * there (we don't check for errors because the user might have
5410 * some private files there that we don't want to delete) */
5411 Utf8Str logFolder;
5412 getLogFolder(logFolder);
5413 Assert(logFolder.length());
5414 if (RTDirExists(logFolder.c_str()))
5415 {
5416 /* Delete all VBox.log[.N] files from the Logs folder
5417 * (this must be in sync with the rotation logic in
5418 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5419 * files that may have been created by the GUI. */
5420 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5421 logFolder.c_str(), RTPATH_DELIMITER);
5422 RTFileDelete(log.c_str());
5423 log = Utf8StrFmt("%s%cVBox.png",
5424 logFolder.c_str(), RTPATH_DELIMITER);
5425 RTFileDelete(log.c_str());
5426 for (int i = uLogHistoryCount; i > 0; i--)
5427 {
5428 log = Utf8StrFmt("%s%cVBox.log.%d",
5429 logFolder.c_str(), RTPATH_DELIMITER, i);
5430 RTFileDelete(log.c_str());
5431 log = Utf8StrFmt("%s%cVBox.png.%d",
5432 logFolder.c_str(), RTPATH_DELIMITER, i);
5433 RTFileDelete(log.c_str());
5434 }
5435
5436 RTDirRemove(logFolder.c_str());
5437 }
5438
5439 /* delete the Snapshots folder, nothing important should be left
5440 * there (we don't check for errors because the user might have
5441 * some private files there that we don't want to delete) */
5442 Utf8Str strFullSnapshotFolder;
5443 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5444 Assert(!strFullSnapshotFolder.isEmpty());
5445 if (RTDirExists(strFullSnapshotFolder.c_str()))
5446 RTDirRemove(strFullSnapshotFolder.c_str());
5447
5448 // delete the directory that contains the settings file, but only
5449 // if it matches the VM name
5450 Utf8Str settingsDir;
5451 if (isInOwnDir(&settingsDir))
5452 RTDirRemove(settingsDir.c_str());
5453 }
5454
5455 alock.release();
5456
5457 mParent->saveModifiedRegistries();
5458 }
5459 catch (HRESULT aRC) { rc = aRC; }
5460
5461 return rc;
5462}
5463
5464STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
5465{
5466 CheckComArgOutPointerValid(aSnapshot);
5467
5468 AutoCaller autoCaller(this);
5469 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5470
5471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5472
5473 ComObjPtr<Snapshot> pSnapshot;
5474 HRESULT rc;
5475
5476 if (!aNameOrId || !*aNameOrId)
5477 // null case (caller wants root snapshot): findSnapshotById() handles this
5478 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5479 else
5480 {
5481 Guid uuid(aNameOrId);
5482 if (uuid.isValid())
5483 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5484 else
5485 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */);
5486 }
5487 pSnapshot.queryInterfaceTo(aSnapshot);
5488
5489 return rc;
5490}
5491
5492STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount)
5493{
5494 CheckComArgStrNotEmptyOrNull(aName);
5495 CheckComArgStrNotEmptyOrNull(aHostPath);
5496
5497 AutoCaller autoCaller(this);
5498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5499
5500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5501
5502 HRESULT rc = checkStateDependency(MutableStateDep);
5503 if (FAILED(rc)) return rc;
5504
5505 Utf8Str strName(aName);
5506
5507 ComObjPtr<SharedFolder> sharedFolder;
5508 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */);
5509 if (SUCCEEDED(rc))
5510 return setError(VBOX_E_OBJECT_IN_USE,
5511 tr("Shared folder named '%s' already exists"),
5512 strName.c_str());
5513
5514 sharedFolder.createObject();
5515 rc = sharedFolder->init(getMachine(),
5516 strName,
5517 aHostPath,
5518 !!aWritable,
5519 !!aAutoMount,
5520 true /* fFailOnError */);
5521 if (FAILED(rc)) return rc;
5522
5523 setModified(IsModified_SharedFolders);
5524 mHWData.backup();
5525 mHWData->mSharedFolders.push_back(sharedFolder);
5526
5527 /* inform the direct session if any */
5528 alock.release();
5529 onSharedFolderChange();
5530
5531 return S_OK;
5532}
5533
5534STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName)
5535{
5536 CheckComArgStrNotEmptyOrNull(aName);
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 ComObjPtr<SharedFolder> sharedFolder;
5547 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */);
5548 if (FAILED(rc)) return rc;
5549
5550 setModified(IsModified_SharedFolders);
5551 mHWData.backup();
5552 mHWData->mSharedFolders.remove(sharedFolder);
5553
5554 /* inform the direct session if any */
5555 alock.release();
5556 onSharedFolderChange();
5557
5558 return S_OK;
5559}
5560
5561STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow)
5562{
5563 CheckComArgOutPointerValid(aCanShow);
5564
5565 /* start with No */
5566 *aCanShow = FALSE;
5567
5568 AutoCaller autoCaller(this);
5569 AssertComRCReturnRC(autoCaller.rc());
5570
5571 ComPtr<IInternalSessionControl> directControl;
5572 {
5573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5574
5575 if (mData->mSession.mState != SessionState_Locked)
5576 return setError(VBOX_E_INVALID_VM_STATE,
5577 tr("Machine is not locked for session (session state: %s)"),
5578 Global::stringifySessionState(mData->mSession.mState));
5579
5580 directControl = mData->mSession.mDirectControl;
5581 }
5582
5583 /* ignore calls made after #OnSessionEnd() is called */
5584 if (!directControl)
5585 return S_OK;
5586
5587 LONG64 dummy;
5588 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5589}
5590
5591STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId)
5592{
5593 CheckComArgOutPointerValid(aWinId);
5594
5595 AutoCaller autoCaller(this);
5596 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
5597
5598 ComPtr<IInternalSessionControl> directControl;
5599 {
5600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5601
5602 if (mData->mSession.mState != SessionState_Locked)
5603 return setError(E_FAIL,
5604 tr("Machine is not locked for session (session state: %s)"),
5605 Global::stringifySessionState(mData->mSession.mState));
5606
5607 directControl = mData->mSession.mDirectControl;
5608 }
5609
5610 /* ignore calls made after #OnSessionEnd() is called */
5611 if (!directControl)
5612 return S_OK;
5613
5614 BOOL dummy;
5615 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5616}
5617
5618#ifdef VBOX_WITH_GUEST_PROPS
5619/**
5620 * Look up a guest property in VBoxSVC's internal structures.
5621 */
5622HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName,
5623 BSTR *aValue,
5624 LONG64 *aTimestamp,
5625 BSTR *aFlags) const
5626{
5627 using namespace guestProp;
5628
5629 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5630 Utf8Str strName(aName);
5631 HWData::GuestPropertyMap::const_iterator it =
5632 mHWData->mGuestProperties.find(strName);
5633
5634 if (it != mHWData->mGuestProperties.end())
5635 {
5636 char szFlags[MAX_FLAGS_LEN + 1];
5637 it->second.strValue.cloneTo(aValue);
5638 *aTimestamp = it->second.mTimestamp;
5639 writeFlags(it->second.mFlags, szFlags);
5640 Bstr(szFlags).cloneTo(aFlags);
5641 }
5642
5643 return S_OK;
5644}
5645
5646/**
5647 * Query the VM that a guest property belongs to for the property.
5648 * @returns E_ACCESSDENIED if the VM process is not available or not
5649 * currently handling queries and the lookup should then be done in
5650 * VBoxSVC.
5651 */
5652HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName,
5653 BSTR *aValue,
5654 LONG64 *aTimestamp,
5655 BSTR *aFlags) const
5656{
5657 HRESULT rc;
5658 ComPtr<IInternalSessionControl> directControl;
5659 directControl = mData->mSession.mDirectControl;
5660
5661 /* fail if we were called after #OnSessionEnd() is called. This is a
5662 * silly race condition. */
5663
5664 if (!directControl)
5665 rc = E_ACCESSDENIED;
5666 else
5667 rc = directControl->AccessGuestProperty(aName, NULL, NULL,
5668 false /* isSetter */,
5669 aValue, aTimestamp, aFlags);
5670 return rc;
5671}
5672#endif // VBOX_WITH_GUEST_PROPS
5673
5674STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName,
5675 BSTR *aValue,
5676 LONG64 *aTimestamp,
5677 BSTR *aFlags)
5678{
5679#ifndef VBOX_WITH_GUEST_PROPS
5680 ReturnComNotImplemented();
5681#else // VBOX_WITH_GUEST_PROPS
5682 CheckComArgStrNotEmptyOrNull(aName);
5683 CheckComArgOutPointerValid(aValue);
5684 CheckComArgOutPointerValid(aTimestamp);
5685 CheckComArgOutPointerValid(aFlags);
5686
5687 AutoCaller autoCaller(this);
5688 if (FAILED(autoCaller.rc())) return autoCaller.rc();
5689
5690 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5691 if (rc == E_ACCESSDENIED)
5692 /* The VM is not running or the service is not (yet) accessible */
5693 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5694 return rc;
5695#endif // VBOX_WITH_GUEST_PROPS
5696}
5697
5698STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue)
5699{
5700 LONG64 dummyTimestamp;
5701 Bstr dummyFlags;
5702 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam());
5703}
5704
5705STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp)
5706{
5707 Bstr dummyValue;
5708 Bstr dummyFlags;
5709 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam());
5710}
5711
5712#ifdef VBOX_WITH_GUEST_PROPS
5713/**
5714 * Set a guest property in VBoxSVC's internal structures.
5715 */
5716HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
5717 IN_BSTR aFlags)
5718{
5719 using namespace guestProp;
5720
5721 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5722 HRESULT rc = S_OK;
5723 HWData::GuestProperty property;
5724 property.mFlags = NILFLAG;
5725
5726 rc = checkStateDependency(MutableStateDep);
5727 if (FAILED(rc)) return rc;
5728
5729 try
5730 {
5731 Utf8Str utf8Name(aName);
5732 Utf8Str utf8Flags(aFlags);
5733 uint32_t fFlags = NILFLAG;
5734 if ( (aFlags != NULL)
5735 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))
5736 )
5737 return setError(E_INVALIDARG,
5738 tr("Invalid guest property flag values: '%ls'"),
5739 aFlags);
5740
5741 HWData::GuestPropertyMap::iterator it =
5742 mHWData->mGuestProperties.find(utf8Name);
5743
5744 if (it == mHWData->mGuestProperties.end())
5745 {
5746 setModified(IsModified_MachineData);
5747 mHWData.backupEx();
5748
5749 RTTIMESPEC time;
5750 HWData::GuestProperty prop;
5751 prop.strValue = aValue;
5752 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5753 prop.mFlags = fFlags;
5754
5755 mHWData->mGuestProperties[Utf8Str(aName)] = prop;
5756 }
5757 else
5758 {
5759 if (it->second.mFlags & (RDONLYHOST))
5760 {
5761 rc = setError(E_ACCESSDENIED,
5762 tr("The property '%ls' cannot be changed by the host"),
5763 aName);
5764 }
5765 else
5766 {
5767 setModified(IsModified_MachineData);
5768 mHWData.backupEx();
5769
5770 /* The backupEx() operation invalidates our iterator,
5771 * so get a new one. */
5772 it = mHWData->mGuestProperties.find(utf8Name);
5773 Assert(it != mHWData->mGuestProperties.end());
5774
5775 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
5776 {
5777 RTTIMESPEC time;
5778 it->second.strValue = aValue;
5779 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5780 if (aFlags != NULL)
5781 it->second.mFlags = fFlags;
5782 }
5783 else
5784 {
5785 mHWData->mGuestProperties.erase(it);
5786 }
5787 }
5788 }
5789
5790 if ( SUCCEEDED(rc)
5791 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
5792 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
5793 RTSTR_MAX,
5794 utf8Name.c_str(),
5795 RTSTR_MAX,
5796 NULL)
5797 )
5798 )
5799 {
5800 alock.release();
5801
5802 mParent->onGuestPropertyChange(mData->mUuid, aName,
5803 aValue ? aValue : Bstr("").raw(),
5804 aFlags ? aFlags : Bstr("").raw());
5805 }
5806 }
5807 catch (std::bad_alloc &)
5808 {
5809 rc = E_OUTOFMEMORY;
5810 }
5811
5812 return rc;
5813}
5814
5815/**
5816 * Set a property on the VM that that property belongs to.
5817 * @returns E_ACCESSDENIED if the VM process is not available or not
5818 * currently handling queries and the setting should then be done in
5819 * VBoxSVC.
5820 */
5821HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
5822 IN_BSTR aFlags)
5823{
5824 HRESULT rc;
5825
5826 try
5827 {
5828 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
5829
5830 BSTR dummy = NULL; /* will not be changed (setter) */
5831 LONG64 dummy64;
5832 if (!directControl)
5833 rc = E_ACCESSDENIED;
5834 else
5835 {
5836 /** @todo Fix when adding DeleteGuestProperty(),
5837 see defect. */
5838 rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
5839 true /* isSetter */,
5840 &dummy, &dummy64, &dummy);
5841 if (FAILED(rc))
5842 {
5843 /* testbox hacking: a shot in the dark. */
5844 ErrorInfoKeeper eik;
5845 return rc;
5846 }
5847 }
5848 }
5849 catch (std::bad_alloc &)
5850 {
5851 rc = E_OUTOFMEMORY;
5852 }
5853
5854 return rc;
5855}
5856#endif // VBOX_WITH_GUEST_PROPS
5857
5858STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
5859 IN_BSTR aFlags)
5860{
5861#ifndef VBOX_WITH_GUEST_PROPS
5862 ReturnComNotImplemented();
5863#else // VBOX_WITH_GUEST_PROPS
5864 CheckComArgStrNotEmptyOrNull(aName);
5865 CheckComArgMaybeNull1(aFlags, 0x80face03);
5866 CheckComArgMaybeNull1(aValue, 0x80face04);
5867
5868 AutoCaller autoCaller(this);
5869 if (FAILED(autoCaller.rc()))
5870 return autoCaller.rc();
5871
5872 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
5873 if (rc == E_ACCESSDENIED)
5874 /* The VM is not running or the service is not (yet) accessible */
5875 rc = setGuestPropertyToService(aName, aValue, aFlags);
5876 return rc;
5877#endif // VBOX_WITH_GUEST_PROPS
5878}
5879
5880STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue)
5881{
5882 return SetGuestProperty(aName, aValue, NULL);
5883}
5884
5885STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName)
5886{
5887 HRESULT hrc = SetGuestProperty(aName, NULL, NULL);
5888 LogRel(("DeleteGuestProperty: %ls -> %Rhrc\n", aName, hrc)); /* !REMOVE ME! Debugging testboxes! */
5889 return hrc;
5890}
5891
5892#ifdef VBOX_WITH_GUEST_PROPS
5893/**
5894 * Enumerate the guest properties in VBoxSVC's internal structures.
5895 */
5896HRESULT Machine::enumerateGuestPropertiesInService
5897 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5898 ComSafeArrayOut(BSTR, aValues),
5899 ComSafeArrayOut(LONG64, aTimestamps),
5900 ComSafeArrayOut(BSTR, aFlags))
5901{
5902 using namespace guestProp;
5903
5904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5905 Utf8Str strPatterns(aPatterns);
5906
5907 HWData::GuestPropertyMap propMap;
5908
5909 /*
5910 * Look for matching patterns and build up a list.
5911 */
5912 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5913 while (it != mHWData->mGuestProperties.end())
5914 {
5915 if ( strPatterns.isEmpty()
5916 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5917 RTSTR_MAX,
5918 it->first.c_str(),
5919 RTSTR_MAX,
5920 NULL)
5921 )
5922 {
5923 propMap.insert(*it);
5924 }
5925
5926 it++;
5927 }
5928
5929 alock.release();
5930
5931 /*
5932 * And build up the arrays for returning the property information.
5933 */
5934 size_t cEntries = propMap.size();
5935 SafeArray<BSTR> names(cEntries);
5936 SafeArray<BSTR> values(cEntries);
5937 SafeArray<LONG64> timestamps(cEntries);
5938 SafeArray<BSTR> flags(cEntries);
5939 size_t iProp = 0;
5940
5941 it = propMap.begin();
5942 while (it != propMap.end())
5943 {
5944 char szFlags[MAX_FLAGS_LEN + 1];
5945 it->first.cloneTo(&names[iProp]);
5946 it->second.strValue.cloneTo(&values[iProp]);
5947 timestamps[iProp] = it->second.mTimestamp;
5948 writeFlags(it->second.mFlags, szFlags);
5949 Bstr(szFlags).cloneTo(&flags[iProp++]);
5950 it++;
5951 }
5952 names.detachTo(ComSafeArrayOutArg(aNames));
5953 values.detachTo(ComSafeArrayOutArg(aValues));
5954 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
5955 flags.detachTo(ComSafeArrayOutArg(aFlags));
5956 return S_OK;
5957}
5958
5959/**
5960 * Enumerate the properties managed by a VM.
5961 * @returns E_ACCESSDENIED if the VM process is not available or not
5962 * currently handling queries and the setting should then be done in
5963 * VBoxSVC.
5964 */
5965HRESULT Machine::enumerateGuestPropertiesOnVM
5966 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames),
5967 ComSafeArrayOut(BSTR, aValues),
5968 ComSafeArrayOut(LONG64, aTimestamps),
5969 ComSafeArrayOut(BSTR, aFlags))
5970{
5971 HRESULT rc;
5972 ComPtr<IInternalSessionControl> directControl;
5973 directControl = mData->mSession.mDirectControl;
5974
5975 if (!directControl)
5976 rc = E_ACCESSDENIED;
5977 else
5978 rc = directControl->EnumerateGuestProperties
5979 (aPatterns, ComSafeArrayOutArg(aNames),
5980 ComSafeArrayOutArg(aValues),
5981 ComSafeArrayOutArg(aTimestamps),
5982 ComSafeArrayOutArg(aFlags));
5983 return rc;
5984}
5985#endif // VBOX_WITH_GUEST_PROPS
5986
5987STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
5988 ComSafeArrayOut(BSTR, aNames),
5989 ComSafeArrayOut(BSTR, aValues),
5990 ComSafeArrayOut(LONG64, aTimestamps),
5991 ComSafeArrayOut(BSTR, aFlags))
5992{
5993#ifndef VBOX_WITH_GUEST_PROPS
5994 ReturnComNotImplemented();
5995#else // VBOX_WITH_GUEST_PROPS
5996 CheckComArgMaybeNull(aPatterns);
5997 CheckComArgOutSafeArrayPointerValid(aNames);
5998 CheckComArgOutSafeArrayPointerValid(aValues);
5999 CheckComArgOutSafeArrayPointerValid(aTimestamps);
6000 CheckComArgOutSafeArrayPointerValid(aFlags);
6001
6002 AutoCaller autoCaller(this);
6003 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6004
6005 HRESULT rc = enumerateGuestPropertiesOnVM
6006 (aPatterns, ComSafeArrayOutArg(aNames),
6007 ComSafeArrayOutArg(aValues),
6008 ComSafeArrayOutArg(aTimestamps),
6009 ComSafeArrayOutArg(aFlags));
6010 if (rc == E_ACCESSDENIED)
6011 /* The VM is not running or the service is not (yet) accessible */
6012 rc = enumerateGuestPropertiesInService
6013 (aPatterns, ComSafeArrayOutArg(aNames),
6014 ComSafeArrayOutArg(aValues),
6015 ComSafeArrayOutArg(aTimestamps),
6016 ComSafeArrayOutArg(aFlags));
6017 return rc;
6018#endif // VBOX_WITH_GUEST_PROPS
6019}
6020
6021STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName,
6022 ComSafeArrayOut(IMediumAttachment*, aAttachments))
6023{
6024 MediaData::AttachmentList atts;
6025
6026 HRESULT rc = getMediumAttachmentsOfController(aName, atts);
6027 if (FAILED(rc)) return rc;
6028
6029 SafeIfaceArray<IMediumAttachment> attachments(atts);
6030 attachments.detachTo(ComSafeArrayOutArg(aAttachments));
6031
6032 return S_OK;
6033}
6034
6035STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName,
6036 LONG aControllerPort,
6037 LONG aDevice,
6038 IMediumAttachment **aAttachment)
6039{
6040 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
6041 aControllerName, aControllerPort, aDevice));
6042
6043 CheckComArgStrNotEmptyOrNull(aControllerName);
6044 CheckComArgOutPointerValid(aAttachment);
6045
6046 AutoCaller autoCaller(this);
6047 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6048
6049 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6050
6051 *aAttachment = NULL;
6052
6053 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments,
6054 aControllerName,
6055 aControllerPort,
6056 aDevice);
6057 if (pAttach.isNull())
6058 return setError(VBOX_E_OBJECT_NOT_FOUND,
6059 tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
6060 aDevice, aControllerPort, aControllerName);
6061
6062 pAttach.queryInterfaceTo(aAttachment);
6063
6064 return S_OK;
6065}
6066
6067STDMETHODIMP Machine::AddStorageController(IN_BSTR aName,
6068 StorageBus_T aConnectionType,
6069 IStorageController **controller)
6070{
6071 CheckComArgStrNotEmptyOrNull(aName);
6072
6073 if ( (aConnectionType <= StorageBus_Null)
6074 || (aConnectionType > StorageBus_SAS))
6075 return setError(E_INVALIDARG,
6076 tr("Invalid connection type: %d"),
6077 aConnectionType);
6078
6079 AutoCaller autoCaller(this);
6080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6081
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 HRESULT rc = checkStateDependency(MutableStateDep);
6085 if (FAILED(rc)) return rc;
6086
6087 /* try to find one with the name first. */
6088 ComObjPtr<StorageController> ctrl;
6089
6090 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */);
6091 if (SUCCEEDED(rc))
6092 return setError(VBOX_E_OBJECT_IN_USE,
6093 tr("Storage controller named '%ls' already exists"),
6094 aName);
6095
6096 ctrl.createObject();
6097
6098 /* get a new instance number for the storage controller */
6099 ULONG ulInstance = 0;
6100 bool fBootable = true;
6101 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6102 it != mStorageControllers->end();
6103 ++it)
6104 {
6105 if ((*it)->getStorageBus() == aConnectionType)
6106 {
6107 ULONG ulCurInst = (*it)->getInstance();
6108
6109 if (ulCurInst >= ulInstance)
6110 ulInstance = ulCurInst + 1;
6111
6112 /* Only one controller of each type can be marked as bootable. */
6113 if ((*it)->getBootable())
6114 fBootable = false;
6115 }
6116 }
6117
6118 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6119 if (FAILED(rc)) return rc;
6120
6121 setModified(IsModified_Storage);
6122 mStorageControllers.backup();
6123 mStorageControllers->push_back(ctrl);
6124
6125 ctrl.queryInterfaceTo(controller);
6126
6127 /* inform the direct session if any */
6128 alock.release();
6129 onStorageControllerChange();
6130
6131 return S_OK;
6132}
6133
6134STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName,
6135 IStorageController **aStorageController)
6136{
6137 CheckComArgStrNotEmptyOrNull(aName);
6138
6139 AutoCaller autoCaller(this);
6140 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6141
6142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6143
6144 ComObjPtr<StorageController> ctrl;
6145
6146 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6147 if (SUCCEEDED(rc))
6148 ctrl.queryInterfaceTo(aStorageController);
6149
6150 return rc;
6151}
6152
6153STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance,
6154 IStorageController **aStorageController)
6155{
6156 AutoCaller autoCaller(this);
6157 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6158
6159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6160
6161 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6162 it != mStorageControllers->end();
6163 ++it)
6164 {
6165 if ((*it)->getInstance() == aInstance)
6166 {
6167 (*it).queryInterfaceTo(aStorageController);
6168 return S_OK;
6169 }
6170 }
6171
6172 return setError(VBOX_E_OBJECT_NOT_FOUND,
6173 tr("Could not find a storage controller with instance number '%lu'"),
6174 aInstance);
6175}
6176
6177STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable)
6178{
6179 AutoCaller autoCaller(this);
6180 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6181
6182 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6183
6184 HRESULT rc = checkStateDependency(MutableStateDep);
6185 if (FAILED(rc)) return rc;
6186
6187 ComObjPtr<StorageController> ctrl;
6188
6189 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6190 if (SUCCEEDED(rc))
6191 {
6192 /* Ensure that only one controller of each type is marked as bootable. */
6193 if (fBootable == TRUE)
6194 {
6195 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6196 it != mStorageControllers->end();
6197 ++it)
6198 {
6199 ComObjPtr<StorageController> aCtrl = (*it);
6200
6201 if ( (aCtrl->getName() != Utf8Str(aName))
6202 && aCtrl->getBootable() == TRUE
6203 && aCtrl->getStorageBus() == ctrl->getStorageBus()
6204 && aCtrl->getControllerType() == ctrl->getControllerType())
6205 {
6206 aCtrl->setBootable(FALSE);
6207 break;
6208 }
6209 }
6210 }
6211
6212 if (SUCCEEDED(rc))
6213 {
6214 ctrl->setBootable(fBootable);
6215 setModified(IsModified_Storage);
6216 }
6217 }
6218
6219 if (SUCCEEDED(rc))
6220 {
6221 /* inform the direct session if any */
6222 alock.release();
6223 onStorageControllerChange();
6224 }
6225
6226 return rc;
6227}
6228
6229STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName)
6230{
6231 CheckComArgStrNotEmptyOrNull(aName);
6232
6233 AutoCaller autoCaller(this);
6234 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6235
6236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6237
6238 HRESULT rc = checkStateDependency(MutableStateDep);
6239 if (FAILED(rc)) return rc;
6240
6241 ComObjPtr<StorageController> ctrl;
6242 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */);
6243 if (FAILED(rc)) return rc;
6244
6245 {
6246 /* find all attached devices to the appropriate storage controller and detach them all */
6247 // make a temporary list because detachDevice invalidates iterators into
6248 // mMediaData->mAttachments
6249 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6250
6251 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6252 it != llAttachments2.end();
6253 ++it)
6254 {
6255 MediumAttachment *pAttachTemp = *it;
6256
6257 AutoCaller localAutoCaller(pAttachTemp);
6258 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6259
6260 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6261
6262 if (pAttachTemp->getControllerName() == aName)
6263 {
6264 rc = detachDevice(pAttachTemp, alock, NULL);
6265 if (FAILED(rc)) return rc;
6266 }
6267 }
6268 }
6269
6270 /* We can remove it now. */
6271 setModified(IsModified_Storage);
6272 mStorageControllers.backup();
6273
6274 ctrl->unshare();
6275
6276 mStorageControllers->remove(ctrl);
6277
6278 /* inform the direct session if any */
6279 alock.release();
6280 onStorageControllerChange();
6281
6282 return S_OK;
6283}
6284
6285STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId,
6286 ULONG *puOriginX,
6287 ULONG *puOriginY,
6288 ULONG *puWidth,
6289 ULONG *puHeight,
6290 BOOL *pfEnabled)
6291{
6292 LogFlowThisFunc(("\n"));
6293
6294 CheckComArgNotNull(puOriginX);
6295 CheckComArgNotNull(puOriginY);
6296 CheckComArgNotNull(puWidth);
6297 CheckComArgNotNull(puHeight);
6298 CheckComArgNotNull(pfEnabled);
6299
6300 uint32_t u32OriginX= 0;
6301 uint32_t u32OriginY= 0;
6302 uint32_t u32Width = 0;
6303 uint32_t u32Height = 0;
6304 uint16_t u16Flags = 0;
6305
6306 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId,
6307 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6308 if (RT_FAILURE(vrc))
6309 {
6310#ifdef RT_OS_WINDOWS
6311 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6312 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6313 * So just assign fEnable to TRUE again.
6314 * The right fix would be to change GUI API wrappers to make sure that parameters
6315 * are changed only if API succeeds.
6316 */
6317 *pfEnabled = TRUE;
6318#endif
6319 return setError(VBOX_E_IPRT_ERROR,
6320 tr("Saved guest size is not available (%Rrc)"),
6321 vrc);
6322 }
6323
6324 *puOriginX = u32OriginX;
6325 *puOriginY = u32OriginY;
6326 *puWidth = u32Width;
6327 *puHeight = u32Height;
6328 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6329
6330 return S_OK;
6331}
6332
6333STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6334{
6335 LogFlowThisFunc(("\n"));
6336
6337 CheckComArgNotNull(aSize);
6338 CheckComArgNotNull(aWidth);
6339 CheckComArgNotNull(aHeight);
6340
6341 if (aScreenId != 0)
6342 return E_NOTIMPL;
6343
6344 AutoCaller autoCaller(this);
6345 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6346
6347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6348
6349 uint8_t *pu8Data = NULL;
6350 uint32_t cbData = 0;
6351 uint32_t u32Width = 0;
6352 uint32_t u32Height = 0;
6353
6354 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6355
6356 if (RT_FAILURE(vrc))
6357 return setError(VBOX_E_IPRT_ERROR,
6358 tr("Saved screenshot data is not available (%Rrc)"),
6359 vrc);
6360
6361 *aSize = cbData;
6362 *aWidth = u32Width;
6363 *aHeight = u32Height;
6364
6365 freeSavedDisplayScreenshot(pu8Data);
6366
6367 return S_OK;
6368}
6369
6370STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6371{
6372 LogFlowThisFunc(("\n"));
6373
6374 CheckComArgNotNull(aWidth);
6375 CheckComArgNotNull(aHeight);
6376 CheckComArgOutSafeArrayPointerValid(aData);
6377
6378 if (aScreenId != 0)
6379 return E_NOTIMPL;
6380
6381 AutoCaller autoCaller(this);
6382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6383
6384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6385
6386 uint8_t *pu8Data = NULL;
6387 uint32_t cbData = 0;
6388 uint32_t u32Width = 0;
6389 uint32_t u32Height = 0;
6390
6391 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6392
6393 if (RT_FAILURE(vrc))
6394 return setError(VBOX_E_IPRT_ERROR,
6395 tr("Saved screenshot data is not available (%Rrc)"),
6396 vrc);
6397
6398 *aWidth = u32Width;
6399 *aHeight = u32Height;
6400
6401 com::SafeArray<BYTE> bitmap(cbData);
6402 /* Convert pixels to format expected by the API caller. */
6403 if (aBGR)
6404 {
6405 /* [0] B, [1] G, [2] R, [3] A. */
6406 for (unsigned i = 0; i < cbData; i += 4)
6407 {
6408 bitmap[i] = pu8Data[i];
6409 bitmap[i + 1] = pu8Data[i + 1];
6410 bitmap[i + 2] = pu8Data[i + 2];
6411 bitmap[i + 3] = 0xff;
6412 }
6413 }
6414 else
6415 {
6416 /* [0] R, [1] G, [2] B, [3] A. */
6417 for (unsigned i = 0; i < cbData; i += 4)
6418 {
6419 bitmap[i] = pu8Data[i + 2];
6420 bitmap[i + 1] = pu8Data[i + 1];
6421 bitmap[i + 2] = pu8Data[i];
6422 bitmap[i + 3] = 0xff;
6423 }
6424 }
6425 bitmap.detachTo(ComSafeArrayOutArg(aData));
6426
6427 freeSavedDisplayScreenshot(pu8Data);
6428
6429 return S_OK;
6430}
6431
6432
6433STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6434{
6435 LogFlowThisFunc(("\n"));
6436
6437 CheckComArgNotNull(aWidth);
6438 CheckComArgNotNull(aHeight);
6439 CheckComArgOutSafeArrayPointerValid(aData);
6440
6441 if (aScreenId != 0)
6442 return E_NOTIMPL;
6443
6444 AutoCaller autoCaller(this);
6445 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6446
6447 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6448
6449 uint8_t *pu8Data = NULL;
6450 uint32_t cbData = 0;
6451 uint32_t u32Width = 0;
6452 uint32_t u32Height = 0;
6453
6454 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6455
6456 if (RT_FAILURE(vrc))
6457 return setError(VBOX_E_IPRT_ERROR,
6458 tr("Saved screenshot data is not available (%Rrc)"),
6459 vrc);
6460
6461 *aWidth = u32Width;
6462 *aHeight = u32Height;
6463
6464 HRESULT rc = S_OK;
6465 uint8_t *pu8PNG = NULL;
6466 uint32_t cbPNG = 0;
6467 uint32_t cxPNG = 0;
6468 uint32_t cyPNG = 0;
6469
6470 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6471
6472 if (RT_SUCCESS(vrc))
6473 {
6474 com::SafeArray<BYTE> screenData(cbPNG);
6475 screenData.initFrom(pu8PNG, cbPNG);
6476 if (pu8PNG)
6477 RTMemFree(pu8PNG);
6478 screenData.detachTo(ComSafeArrayOutArg(aData));
6479 }
6480 else
6481 {
6482 if (pu8PNG)
6483 RTMemFree(pu8PNG);
6484 return setError(VBOX_E_IPRT_ERROR,
6485 tr("Could not convert screenshot to PNG (%Rrc)"),
6486 vrc);
6487 }
6488
6489 freeSavedDisplayScreenshot(pu8Data);
6490
6491 return rc;
6492}
6493
6494STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)
6495{
6496 LogFlowThisFunc(("\n"));
6497
6498 CheckComArgNotNull(aSize);
6499 CheckComArgNotNull(aWidth);
6500 CheckComArgNotNull(aHeight);
6501
6502 if (aScreenId != 0)
6503 return E_NOTIMPL;
6504
6505 AutoCaller autoCaller(this);
6506 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6507
6508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6509
6510 uint8_t *pu8Data = NULL;
6511 uint32_t cbData = 0;
6512 uint32_t u32Width = 0;
6513 uint32_t u32Height = 0;
6514
6515 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6516
6517 if (RT_FAILURE(vrc))
6518 return setError(VBOX_E_IPRT_ERROR,
6519 tr("Saved screenshot data is not available (%Rrc)"),
6520 vrc);
6521
6522 *aSize = cbData;
6523 *aWidth = u32Width;
6524 *aHeight = u32Height;
6525
6526 freeSavedDisplayScreenshot(pu8Data);
6527
6528 return S_OK;
6529}
6530
6531STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData))
6532{
6533 LogFlowThisFunc(("\n"));
6534
6535 CheckComArgNotNull(aWidth);
6536 CheckComArgNotNull(aHeight);
6537 CheckComArgOutSafeArrayPointerValid(aData);
6538
6539 if (aScreenId != 0)
6540 return E_NOTIMPL;
6541
6542 AutoCaller autoCaller(this);
6543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6544
6545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6546
6547 uint8_t *pu8Data = NULL;
6548 uint32_t cbData = 0;
6549 uint32_t u32Width = 0;
6550 uint32_t u32Height = 0;
6551
6552 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6553
6554 if (RT_FAILURE(vrc))
6555 return setError(VBOX_E_IPRT_ERROR,
6556 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6557 vrc);
6558
6559 *aWidth = u32Width;
6560 *aHeight = u32Height;
6561
6562 com::SafeArray<BYTE> png(cbData);
6563 png.initFrom(pu8Data, cbData);
6564 png.detachTo(ComSafeArrayOutArg(aData));
6565
6566 freeSavedDisplayScreenshot(pu8Data);
6567
6568 return S_OK;
6569}
6570
6571STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu)
6572{
6573 HRESULT rc = S_OK;
6574 LogFlowThisFunc(("\n"));
6575
6576 AutoCaller autoCaller(this);
6577 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6578
6579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6580
6581 if (!mHWData->mCPUHotPlugEnabled)
6582 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6583
6584 if (aCpu >= mHWData->mCPUCount)
6585 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6586
6587 if (mHWData->mCPUAttached[aCpu])
6588 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6589
6590 alock.release();
6591 rc = onCPUChange(aCpu, false);
6592 alock.acquire();
6593 if (FAILED(rc)) return rc;
6594
6595 setModified(IsModified_MachineData);
6596 mHWData.backup();
6597 mHWData->mCPUAttached[aCpu] = true;
6598
6599 /* Save settings if online */
6600 if (Global::IsOnline(mData->mMachineState))
6601 saveSettings(NULL);
6602
6603 return S_OK;
6604}
6605
6606STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu)
6607{
6608 HRESULT rc = S_OK;
6609 LogFlowThisFunc(("\n"));
6610
6611 AutoCaller autoCaller(this);
6612 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6613
6614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6615
6616 if (!mHWData->mCPUHotPlugEnabled)
6617 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6618
6619 if (aCpu >= SchemaDefs::MaxCPUCount)
6620 return setError(E_INVALIDARG,
6621 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6622 SchemaDefs::MaxCPUCount);
6623
6624 if (!mHWData->mCPUAttached[aCpu])
6625 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6626
6627 /* CPU 0 can't be detached */
6628 if (aCpu == 0)
6629 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6630
6631 alock.release();
6632 rc = onCPUChange(aCpu, true);
6633 alock.acquire();
6634 if (FAILED(rc)) return rc;
6635
6636 setModified(IsModified_MachineData);
6637 mHWData.backup();
6638 mHWData->mCPUAttached[aCpu] = false;
6639
6640 /* Save settings if online */
6641 if (Global::IsOnline(mData->mMachineState))
6642 saveSettings(NULL);
6643
6644 return S_OK;
6645}
6646
6647STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached)
6648{
6649 LogFlowThisFunc(("\n"));
6650
6651 CheckComArgNotNull(aCpuAttached);
6652
6653 *aCpuAttached = false;
6654
6655 AutoCaller autoCaller(this);
6656 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6657
6658 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6659
6660 /* If hotplug is enabled the CPU is always enabled. */
6661 if (!mHWData->mCPUHotPlugEnabled)
6662 {
6663 if (aCpu < mHWData->mCPUCount)
6664 *aCpuAttached = true;
6665 }
6666 else
6667 {
6668 if (aCpu < SchemaDefs::MaxCPUCount)
6669 *aCpuAttached = mHWData->mCPUAttached[aCpu];
6670 }
6671
6672 return S_OK;
6673}
6674
6675STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName)
6676{
6677 CheckComArgOutPointerValid(aName);
6678
6679 AutoCaller autoCaller(this);
6680 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6681
6682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 Utf8Str log = queryLogFilename(aIdx);
6685 if (!RTFileExists(log.c_str()))
6686 log.setNull();
6687 log.cloneTo(aName);
6688
6689 return S_OK;
6690}
6691
6692STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
6693{
6694 LogFlowThisFunc(("\n"));
6695 CheckComArgOutSafeArrayPointerValid(aData);
6696 if (aSize < 0)
6697 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6698
6699 AutoCaller autoCaller(this);
6700 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6701
6702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6703
6704 HRESULT rc = S_OK;
6705 Utf8Str log = queryLogFilename(aIdx);
6706
6707 /* do not unnecessarily hold the lock while doing something which does
6708 * not need the lock and potentially takes a long time. */
6709 alock.release();
6710
6711 /* Limit the chunk size to 32K for now, as that gives better performance
6712 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6713 * One byte expands to approx. 25 bytes of breathtaking XML. */
6714 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6715 com::SafeArray<BYTE> logData(cbData);
6716
6717 RTFILE LogFile;
6718 int vrc = RTFileOpen(&LogFile, log.c_str(),
6719 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6720 if (RT_SUCCESS(vrc))
6721 {
6722 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData);
6723 if (RT_SUCCESS(vrc))
6724 logData.resize(cbData);
6725 else
6726 rc = setError(VBOX_E_IPRT_ERROR,
6727 tr("Could not read log file '%s' (%Rrc)"),
6728 log.c_str(), vrc);
6729 RTFileClose(LogFile);
6730 }
6731 else
6732 rc = setError(VBOX_E_IPRT_ERROR,
6733 tr("Could not open log file '%s' (%Rrc)"),
6734 log.c_str(), vrc);
6735
6736 if (FAILED(rc))
6737 logData.resize(0);
6738 logData.detachTo(ComSafeArrayOutArg(aData));
6739
6740 return rc;
6741}
6742
6743
6744/**
6745 * Currently this method doesn't attach device to the running VM,
6746 * just makes sure it's plugged on next VM start.
6747 */
6748STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
6749{
6750 AutoCaller autoCaller(this);
6751 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6752
6753 // lock scope
6754 {
6755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6756
6757 HRESULT rc = checkStateDependency(MutableStateDep);
6758 if (FAILED(rc)) return rc;
6759
6760 ChipsetType_T aChipset = ChipsetType_PIIX3;
6761 COMGETTER(ChipsetType)(&aChipset);
6762
6763 if (aChipset != ChipsetType_ICH9)
6764 {
6765 return setError(E_INVALIDARG,
6766 tr("Host PCI attachment only supported with ICH9 chipset"));
6767 }
6768
6769 // check if device with this host PCI address already attached
6770 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6771 it != mHWData->mPCIDeviceAssignments.end();
6772 ++it)
6773 {
6774 LONG iHostAddress = -1;
6775 ComPtr<PCIDeviceAttachment> pAttach;
6776 pAttach = *it;
6777 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6778 if (iHostAddress == hostAddress)
6779 return setError(E_INVALIDARG,
6780 tr("Device with host PCI address already attached to this VM"));
6781 }
6782
6783 ComObjPtr<PCIDeviceAttachment> pda;
6784 char name[32];
6785
6786 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
6787 Bstr bname(name);
6788 pda.createObject();
6789 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
6790 setModified(IsModified_MachineData);
6791 mHWData.backup();
6792 mHWData->mPCIDeviceAssignments.push_back(pda);
6793 }
6794
6795 return S_OK;
6796}
6797
6798/**
6799 * Currently this method doesn't detach device from the running VM,
6800 * just makes sure it's not plugged on next VM start.
6801 */
6802STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress)
6803{
6804 AutoCaller autoCaller(this);
6805 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6806
6807 ComObjPtr<PCIDeviceAttachment> pAttach;
6808 bool fRemoved = false;
6809 HRESULT rc;
6810
6811 // lock scope
6812 {
6813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6814
6815 rc = checkStateDependency(MutableStateDep);
6816 if (FAILED(rc)) return rc;
6817
6818 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6819 it != mHWData->mPCIDeviceAssignments.end();
6820 ++it)
6821 {
6822 LONG iHostAddress = -1;
6823 pAttach = *it;
6824 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6825 if (iHostAddress != -1 && iHostAddress == hostAddress)
6826 {
6827 setModified(IsModified_MachineData);
6828 mHWData.backup();
6829 mHWData->mPCIDeviceAssignments.remove(pAttach);
6830 fRemoved = true;
6831 break;
6832 }
6833 }
6834 }
6835
6836
6837 /* Fire event outside of the lock */
6838 if (fRemoved)
6839 {
6840 Assert(!pAttach.isNull());
6841 ComPtr<IEventSource> es;
6842 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6843 Assert(SUCCEEDED(rc));
6844 Bstr mid;
6845 rc = this->COMGETTER(Id)(mid.asOutParam());
6846 Assert(SUCCEEDED(rc));
6847 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6848 }
6849
6850 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6851 tr("No host PCI device %08x attached"),
6852 hostAddress
6853 );
6854}
6855
6856STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments))
6857{
6858 CheckComArgOutSafeArrayPointerValid(aAssignments);
6859
6860 AutoCaller autoCaller(this);
6861 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6862
6863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6864
6865 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments);
6866 assignments.detachTo(ComSafeArrayOutArg(aAssignments));
6867
6868 return S_OK;
6869}
6870
6871STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl)
6872{
6873 CheckComArgOutPointerValid(aBandwidthControl);
6874
6875 AutoCaller autoCaller(this);
6876 if (FAILED(autoCaller.rc())) return autoCaller.rc();
6877
6878 mBandwidthControl.queryInterfaceTo(aBandwidthControl);
6879
6880 return S_OK;
6881}
6882
6883STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled)
6884{
6885 CheckComArgOutPointerValid(pfEnabled);
6886 AutoCaller autoCaller(this);
6887 HRESULT hrc = autoCaller.rc();
6888 if (SUCCEEDED(hrc))
6889 {
6890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6891 *pfEnabled = mHWData->mDebugging.fTracingEnabled;
6892 }
6893 return hrc;
6894}
6895
6896STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled)
6897{
6898 AutoCaller autoCaller(this);
6899 HRESULT hrc = autoCaller.rc();
6900 if (SUCCEEDED(hrc))
6901 {
6902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6903 hrc = checkStateDependency(MutableStateDep);
6904 if (SUCCEEDED(hrc))
6905 {
6906 hrc = mHWData.backupEx();
6907 if (SUCCEEDED(hrc))
6908 {
6909 setModified(IsModified_MachineData);
6910 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE;
6911 }
6912 }
6913 }
6914 return hrc;
6915}
6916
6917STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig)
6918{
6919 CheckComArgOutPointerValid(pbstrConfig);
6920 AutoCaller autoCaller(this);
6921 HRESULT hrc = autoCaller.rc();
6922 if (SUCCEEDED(hrc))
6923 {
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig);
6926 }
6927 return hrc;
6928}
6929
6930STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig)
6931{
6932 CheckComArgStr(bstrConfig);
6933 AutoCaller autoCaller(this);
6934 HRESULT hrc = autoCaller.rc();
6935 if (SUCCEEDED(hrc))
6936 {
6937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6938 hrc = checkStateDependency(MutableStateDep);
6939 if (SUCCEEDED(hrc))
6940 {
6941 hrc = mHWData.backupEx();
6942 if (SUCCEEDED(hrc))
6943 {
6944 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig);
6945 if (SUCCEEDED(hrc))
6946 setModified(IsModified_MachineData);
6947 }
6948 }
6949 }
6950 return hrc;
6951
6952}
6953
6954STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow)
6955{
6956 CheckComArgOutPointerValid(pfAllow);
6957 AutoCaller autoCaller(this);
6958 HRESULT hrc = autoCaller.rc();
6959 if (SUCCEEDED(hrc))
6960 {
6961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6962 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM;
6963 }
6964 return hrc;
6965}
6966
6967STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow)
6968{
6969 AutoCaller autoCaller(this);
6970 HRESULT hrc = autoCaller.rc();
6971 if (SUCCEEDED(hrc))
6972 {
6973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6974 hrc = checkStateDependency(MutableStateDep);
6975 if (SUCCEEDED(hrc))
6976 {
6977 hrc = mHWData.backupEx();
6978 if (SUCCEEDED(hrc))
6979 {
6980 setModified(IsModified_MachineData);
6981 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE;
6982 }
6983 }
6984 }
6985 return hrc;
6986}
6987
6988STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled)
6989{
6990 CheckComArgOutPointerValid(pfEnabled);
6991 AutoCaller autoCaller(this);
6992 HRESULT hrc = autoCaller.rc();
6993 if (SUCCEEDED(hrc))
6994 {
6995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6996 *pfEnabled = mHWData->mAutostart.fAutostartEnabled;
6997 }
6998 return hrc;
6999}
7000
7001STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled)
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 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled)
7011 {
7012 AutostartDb *autostartDb = mParent->getAutostartDb();
7013 int vrc;
7014
7015 if (fEnabled)
7016 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7017 else
7018 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7019
7020 if (RT_SUCCESS(vrc))
7021 {
7022 hrc = mHWData.backupEx();
7023 if (SUCCEEDED(hrc))
7024 {
7025 setModified(IsModified_MachineData);
7026 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE;
7027 }
7028 }
7029 else if (vrc == VERR_NOT_SUPPORTED)
7030 hrc = setError(VBOX_E_NOT_SUPPORTED,
7031 tr("The VM autostart feature is not supported on this platform"));
7032 else if (vrc == VERR_PATH_NOT_FOUND)
7033 hrc = setError(E_FAIL,
7034 tr("The path to the autostart database is not set"));
7035 else
7036 hrc = setError(E_UNEXPECTED,
7037 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7038 fEnabled ? "Adding" : "Removing",
7039 mUserData->s.strName.c_str(), vrc);
7040 }
7041 }
7042 return hrc;
7043}
7044
7045STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay)
7046{
7047 CheckComArgOutPointerValid(puDelay);
7048 AutoCaller autoCaller(this);
7049 HRESULT hrc = autoCaller.rc();
7050 if (SUCCEEDED(hrc))
7051 {
7052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7053 *puDelay = mHWData->mAutostart.uAutostartDelay;
7054 }
7055 return hrc;
7056}
7057
7058STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay)
7059{
7060 AutoCaller autoCaller(this);
7061 HRESULT hrc = autoCaller.rc();
7062 if (SUCCEEDED(hrc))
7063 {
7064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7065 hrc = checkStateDependency(MutableStateDep);
7066 if (SUCCEEDED(hrc))
7067 {
7068 hrc = mHWData.backupEx();
7069 if (SUCCEEDED(hrc))
7070 {
7071 setModified(IsModified_MachineData);
7072 mHWData->mAutostart.uAutostartDelay = uDelay;
7073 }
7074 }
7075 }
7076 return hrc;
7077}
7078
7079STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType)
7080{
7081 CheckComArgOutPointerValid(penmAutostopType);
7082 AutoCaller autoCaller(this);
7083 HRESULT hrc = autoCaller.rc();
7084 if (SUCCEEDED(hrc))
7085 {
7086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7087 *penmAutostopType = mHWData->mAutostart.enmAutostopType;
7088 }
7089 return hrc;
7090}
7091
7092STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType)
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 && mHWData->mAutostart.enmAutostopType != enmAutostopType)
7102 {
7103 AutostartDb *autostartDb = mParent->getAutostartDb();
7104 int vrc;
7105
7106 if (enmAutostopType != AutostopType_Disabled)
7107 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7108 else
7109 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7110
7111 if (RT_SUCCESS(vrc))
7112 {
7113 hrc = mHWData.backupEx();
7114 if (SUCCEEDED(hrc))
7115 {
7116 setModified(IsModified_MachineData);
7117 mHWData->mAutostart.enmAutostopType = enmAutostopType;
7118 }
7119 }
7120 else if (vrc == VERR_NOT_SUPPORTED)
7121 hrc = setError(VBOX_E_NOT_SUPPORTED,
7122 tr("The VM autostop feature is not supported on this platform"));
7123 else if (vrc == VERR_PATH_NOT_FOUND)
7124 hrc = setError(E_FAIL,
7125 tr("The path to the autostart database is not set"));
7126 else
7127 hrc = setError(E_UNEXPECTED,
7128 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7129 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7130 mUserData->s.strName.c_str(), vrc);
7131 }
7132 }
7133 return hrc;
7134}
7135
7136STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend)
7137{
7138 CheckComArgOutPointerValid(aDefaultFrontend);
7139 AutoCaller autoCaller(this);
7140 HRESULT hrc = autoCaller.rc();
7141 if (SUCCEEDED(hrc))
7142 {
7143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7144 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend);
7145 }
7146 return hrc;
7147}
7148
7149STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend)
7150{
7151 CheckComArgStr(aDefaultFrontend);
7152 AutoCaller autoCaller(this);
7153 HRESULT hrc = autoCaller.rc();
7154 if (SUCCEEDED(hrc))
7155 {
7156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7157 hrc = checkStateDependency(MutableOrSavedStateDep);
7158 if (SUCCEEDED(hrc))
7159 {
7160 hrc = mHWData.backupEx();
7161 if (SUCCEEDED(hrc))
7162 {
7163 setModified(IsModified_MachineData);
7164 mHWData->mDefaultFrontend = aDefaultFrontend;
7165 }
7166 }
7167 }
7168 return hrc;
7169}
7170
7171
7172STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
7173{
7174 LogFlowFuncEnter();
7175
7176 CheckComArgNotNull(pTarget);
7177 CheckComArgOutPointerValid(pProgress);
7178
7179 /* Convert the options. */
7180 RTCList<CloneOptions_T> optList;
7181 if (options != NULL)
7182 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
7183
7184 if (optList.contains(CloneOptions_Link))
7185 {
7186 if (!isSnapshotMachine())
7187 return setError(E_INVALIDARG,
7188 tr("Linked clone can only be created from a snapshot"));
7189 if (mode != CloneMode_MachineState)
7190 return setError(E_INVALIDARG,
7191 tr("Linked clone can only be created for a single machine state"));
7192 }
7193 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7194
7195 AutoCaller autoCaller(this);
7196 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7197
7198
7199 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
7200
7201 HRESULT rc = pWorker->start(pProgress);
7202
7203 LogFlowFuncLeave();
7204
7205 return rc;
7206}
7207
7208// public methods for internal purposes
7209/////////////////////////////////////////////////////////////////////////////
7210
7211/**
7212 * Adds the given IsModified_* flag to the dirty flags of the machine.
7213 * This must be called either during loadSettings or under the machine write lock.
7214 * @param fl
7215 */
7216void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7217{
7218 mData->flModifications |= fl;
7219 if (fAllowStateModification && isStateModificationAllowed())
7220 mData->mCurrentStateModified = true;
7221}
7222
7223/**
7224 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7225 * care of the write locking.
7226 *
7227 * @param fModifications The flag to add.
7228 */
7229void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7230{
7231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7232 setModified(fModification, fAllowStateModification);
7233}
7234
7235/**
7236 * Saves the registry entry of this machine to the given configuration node.
7237 *
7238 * @param aEntryNode Node to save the registry entry to.
7239 *
7240 * @note locks this object for reading.
7241 */
7242HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)
7243{
7244 AutoLimitedCaller autoCaller(this);
7245 AssertComRCReturnRC(autoCaller.rc());
7246
7247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7248
7249 data.uuid = mData->mUuid;
7250 data.strSettingsFile = mData->m_strConfigFile;
7251
7252 return S_OK;
7253}
7254
7255/**
7256 * Calculates the absolute path of the given path taking the directory of the
7257 * machine settings file as the current directory.
7258 *
7259 * @param aPath Path to calculate the absolute path for.
7260 * @param aResult Where to put the result (used only on success, can be the
7261 * same Utf8Str instance as passed in @a aPath).
7262 * @return IPRT result.
7263 *
7264 * @note Locks this object for reading.
7265 */
7266int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7267{
7268 AutoCaller autoCaller(this);
7269 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7270
7271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7272
7273 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7274
7275 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7276
7277 strSettingsDir.stripFilename();
7278 char folder[RTPATH_MAX];
7279 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7280 if (RT_SUCCESS(vrc))
7281 aResult = folder;
7282
7283 return vrc;
7284}
7285
7286/**
7287 * Copies strSource to strTarget, making it relative to the machine folder
7288 * if it is a subdirectory thereof, or simply copying it otherwise.
7289 *
7290 * @param strSource Path to evaluate and copy.
7291 * @param strTarget Buffer to receive target path.
7292 *
7293 * @note Locks this object for reading.
7294 */
7295void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,
7296 Utf8Str &strTarget)
7297{
7298 AutoCaller autoCaller(this);
7299 AssertComRCReturn(autoCaller.rc(), (void)0);
7300
7301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7302
7303 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7304 // use strTarget as a temporary buffer to hold the machine settings dir
7305 strTarget = mData->m_strConfigFileFull;
7306 strTarget.stripFilename();
7307 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7308 {
7309 // is relative: then append what's left
7310 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7311 // for empty paths (only possible for subdirs) use "." to avoid
7312 // triggering default settings for not present config attributes.
7313 if (strTarget.isEmpty())
7314 strTarget = ".";
7315 }
7316 else
7317 // is not relative: then overwrite
7318 strTarget = strSource;
7319}
7320
7321/**
7322 * Returns the full path to the machine's log folder in the
7323 * \a aLogFolder argument.
7324 */
7325void Machine::getLogFolder(Utf8Str &aLogFolder)
7326{
7327 AutoCaller autoCaller(this);
7328 AssertComRCReturnVoid(autoCaller.rc());
7329
7330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7331
7332 char szTmp[RTPATH_MAX];
7333 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7334 if (RT_SUCCESS(vrc))
7335 {
7336 if (szTmp[0] && !mUserData.isNull())
7337 {
7338 char szTmp2[RTPATH_MAX];
7339 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7340 if (RT_SUCCESS(vrc))
7341 aLogFolder = BstrFmt("%s%c%s",
7342 szTmp2,
7343 RTPATH_DELIMITER,
7344 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7345 }
7346 else
7347 vrc = VERR_PATH_IS_RELATIVE;
7348 }
7349
7350 if (RT_FAILURE(vrc))
7351 {
7352 // fallback if VBOX_USER_LOGHOME is not set or invalid
7353 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7354 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7355 aLogFolder.append(RTPATH_DELIMITER);
7356 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7357 }
7358}
7359
7360/**
7361 * Returns the full path to the machine's log file for an given index.
7362 */
7363Utf8Str Machine::queryLogFilename(ULONG idx)
7364{
7365 Utf8Str logFolder;
7366 getLogFolder(logFolder);
7367 Assert(logFolder.length());
7368 Utf8Str log;
7369 if (idx == 0)
7370 log = Utf8StrFmt("%s%cVBox.log",
7371 logFolder.c_str(), RTPATH_DELIMITER);
7372 else
7373 log = Utf8StrFmt("%s%cVBox.log.%d",
7374 logFolder.c_str(), RTPATH_DELIMITER, idx);
7375 return log;
7376}
7377
7378/**
7379 * Composes a unique saved state filename based on the current system time. The filename is
7380 * granular to the second so this will work so long as no more than one snapshot is taken on
7381 * a machine per second.
7382 *
7383 * Before version 4.1, we used this formula for saved state files:
7384 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7385 * which no longer works because saved state files can now be shared between the saved state of the
7386 * "saved" machine and an online snapshot, and the following would cause problems:
7387 * 1) save machine
7388 * 2) create online snapshot from that machine state --> reusing saved state file
7389 * 3) save machine again --> filename would be reused, breaking the online snapshot
7390 *
7391 * So instead we now use a timestamp.
7392 *
7393 * @param str
7394 */
7395void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
7396{
7397 AutoCaller autoCaller(this);
7398 AssertComRCReturnVoid(autoCaller.rc());
7399
7400 {
7401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7402 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7403 }
7404
7405 RTTIMESPEC ts;
7406 RTTimeNow(&ts);
7407 RTTIME time;
7408 RTTimeExplode(&time, &ts);
7409
7410 strStateFilePath += RTPATH_DELIMITER;
7411 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7412 time.i32Year, time.u8Month, time.u8MonthDay,
7413 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7414}
7415
7416/**
7417 * @note Locks this object for writing, calls the client process
7418 * (inside the lock).
7419 */
7420HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
7421 const Utf8Str &strFrontend,
7422 const Utf8Str &strEnvironment,
7423 ProgressProxy *aProgress)
7424{
7425 LogFlowThisFuncEnter();
7426
7427 AssertReturn(aControl, E_FAIL);
7428 AssertReturn(aProgress, E_FAIL);
7429
7430 AutoCaller autoCaller(this);
7431 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7432
7433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7434
7435 if (!mData->mRegistered)
7436 return setError(E_UNEXPECTED,
7437 tr("The machine '%s' is not registered"),
7438 mUserData->s.strName.c_str());
7439
7440 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7441
7442 if ( mData->mSession.mState == SessionState_Locked
7443 || mData->mSession.mState == SessionState_Spawning
7444 || mData->mSession.mState == SessionState_Unlocking)
7445 return setError(VBOX_E_INVALID_OBJECT_STATE,
7446 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7447 mUserData->s.strName.c_str());
7448
7449 /* may not be busy */
7450 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7451
7452 /* get the path to the executable */
7453 char szPath[RTPATH_MAX];
7454 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7455 size_t sz = strlen(szPath);
7456 szPath[sz++] = RTPATH_DELIMITER;
7457 szPath[sz] = 0;
7458 char *cmd = szPath + sz;
7459 sz = RTPATH_MAX - sz;
7460
7461 int vrc = VINF_SUCCESS;
7462 RTPROCESS pid = NIL_RTPROCESS;
7463
7464 RTENV env = RTENV_DEFAULT;
7465
7466 if (!strEnvironment.isEmpty())
7467 {
7468 char *newEnvStr = NULL;
7469
7470 do
7471 {
7472 /* clone the current environment */
7473 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7474 AssertRCBreakStmt(vrc2, vrc = vrc2);
7475
7476 newEnvStr = RTStrDup(strEnvironment.c_str());
7477 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7478
7479 /* put new variables to the environment
7480 * (ignore empty variable names here since RTEnv API
7481 * intentionally doesn't do that) */
7482 char *var = newEnvStr;
7483 for (char *p = newEnvStr; *p; ++p)
7484 {
7485 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7486 {
7487 *p = '\0';
7488 if (*var)
7489 {
7490 char *val = strchr(var, '=');
7491 if (val)
7492 {
7493 *val++ = '\0';
7494 vrc2 = RTEnvSetEx(env, var, val);
7495 }
7496 else
7497 vrc2 = RTEnvUnsetEx(env, var);
7498 if (RT_FAILURE(vrc2))
7499 break;
7500 }
7501 var = p + 1;
7502 }
7503 }
7504 if (RT_SUCCESS(vrc2) && *var)
7505 vrc2 = RTEnvPutEx(env, var);
7506
7507 AssertRCBreakStmt(vrc2, vrc = vrc2);
7508 }
7509 while (0);
7510
7511 if (newEnvStr != NULL)
7512 RTStrFree(newEnvStr);
7513 }
7514
7515 /* Qt is default */
7516#ifdef VBOX_WITH_QTGUI
7517 if (strFrontend == "gui" || strFrontend == "GUI/Qt" || strFrontend == "")
7518 {
7519# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7520 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM";
7521# else
7522 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7523# endif
7524 Assert(sz >= sizeof(VirtualBox_exe));
7525 strcpy(cmd, VirtualBox_exe);
7526
7527 Utf8Str idStr = mData->mUuid.toString();
7528 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 };
7529 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7530 }
7531#else /* !VBOX_WITH_QTGUI */
7532 if (0)
7533 ;
7534#endif /* VBOX_WITH_QTGUI */
7535
7536 else
7537
7538#ifdef VBOX_WITH_VBOXSDL
7539 if (strFrontend == "sdl" || strFrontend == "GUI/SDL")
7540 {
7541 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7542 Assert(sz >= sizeof(VBoxSDL_exe));
7543 strcpy(cmd, VBoxSDL_exe);
7544
7545 Utf8Str idStr = mData->mUuid.toString();
7546 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
7547 vrc = RTProcCreate(szPath, args, env, 0, &pid);
7548 }
7549#else /* !VBOX_WITH_VBOXSDL */
7550 if (0)
7551 ;
7552#endif /* !VBOX_WITH_VBOXSDL */
7553
7554 else
7555
7556#ifdef VBOX_WITH_HEADLESS
7557 if ( strFrontend == "headless"
7558 || strFrontend == "capture"
7559 || strFrontend == "vrdp" /* Deprecated. Same as headless. */
7560 )
7561 {
7562 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7563 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7564 * and a VM works even if the server has not been installed.
7565 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7566 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7567 * differently in 4.0 and 3.x.
7568 */
7569 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7570 Assert(sz >= sizeof(VBoxHeadless_exe));
7571 strcpy(cmd, VBoxHeadless_exe);
7572
7573 Utf8Str idStr = mData->mUuid.toString();
7574 /* Leave space for "--capture" arg. */
7575 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(),
7576 "--startvm", idStr.c_str(),
7577 "--vrde", "config",
7578 0, /* For "--capture". */
7579 0 };
7580 if (strFrontend == "capture")
7581 {
7582 unsigned pos = RT_ELEMENTS(args) - 2;
7583 args[pos] = "--capture";
7584 }
7585 vrc = RTProcCreate(szPath, args, env,
7586#ifdef RT_OS_WINDOWS
7587 RTPROC_FLAGS_NO_WINDOW
7588#else
7589 0
7590#endif
7591 , &pid);
7592 }
7593#else /* !VBOX_WITH_HEADLESS */
7594 if (0)
7595 ;
7596#endif /* !VBOX_WITH_HEADLESS */
7597 else
7598 {
7599 RTEnvDestroy(env);
7600 return setError(E_INVALIDARG,
7601 tr("Invalid frontend name: '%s'"),
7602 strFrontend.c_str());
7603 }
7604
7605 RTEnvDestroy(env);
7606
7607 if (RT_FAILURE(vrc))
7608 return setError(VBOX_E_IPRT_ERROR,
7609 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7610 mUserData->s.strName.c_str(), vrc);
7611
7612 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7613
7614 /*
7615 * Note that we don't release the lock here before calling the client,
7616 * because it doesn't need to call us back if called with a NULL argument.
7617 * Releasing the lock here is dangerous because we didn't prepare the
7618 * launch data yet, but the client we've just started may happen to be
7619 * too fast and call openSession() that will fail (because of PID, etc.),
7620 * so that the Machine will never get out of the Spawning session state.
7621 */
7622
7623 /* inform the session that it will be a remote one */
7624 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7625 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write);
7626 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7627
7628 if (FAILED(rc))
7629 {
7630 /* restore the session state */
7631 mData->mSession.mState = SessionState_Unlocked;
7632 /* The failure may occur w/o any error info (from RPC), so provide one */
7633 return setError(VBOX_E_VM_ERROR,
7634 tr("Failed to assign the machine to the session (%Rrc)"), rc);
7635 }
7636
7637 /* attach launch data to the machine */
7638 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7639 mData->mSession.mRemoteControls.push_back(aControl);
7640 mData->mSession.mProgress = aProgress;
7641 mData->mSession.mPID = pid;
7642 mData->mSession.mState = SessionState_Spawning;
7643 mData->mSession.mType = strFrontend;
7644
7645 LogFlowThisFuncLeave();
7646 return S_OK;
7647}
7648
7649/**
7650 * Returns @c true if the given machine has an open direct session and returns
7651 * the session machine instance and additional session data (on some platforms)
7652 * if so.
7653 *
7654 * Note that when the method returns @c false, the arguments remain unchanged.
7655 *
7656 * @param aMachine Session machine object.
7657 * @param aControl Direct session control object (optional).
7658 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional).
7659 *
7660 * @note locks this object for reading.
7661 */
7662#if defined(RT_OS_WINDOWS)
7663bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7664 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7665 HANDLE *aIPCSem /*= NULL*/,
7666 bool aAllowClosing /*= false*/)
7667#elif defined(RT_OS_OS2)
7668bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7669 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7670 HMTX *aIPCSem /*= NULL*/,
7671 bool aAllowClosing /*= false*/)
7672#else
7673bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7674 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7675 bool aAllowClosing /*= false*/)
7676#endif
7677{
7678 AutoLimitedCaller autoCaller(this);
7679 AssertComRCReturn(autoCaller.rc(), false);
7680
7681 /* just return false for inaccessible machines */
7682 if (autoCaller.state() != Ready)
7683 return false;
7684
7685 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7686
7687 if ( mData->mSession.mState == SessionState_Locked
7688 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7689 )
7690 {
7691 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7692
7693 aMachine = mData->mSession.mMachine;
7694
7695 if (aControl != NULL)
7696 *aControl = mData->mSession.mDirectControl;
7697
7698#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7699 /* Additional session data */
7700 if (aIPCSem != NULL)
7701 *aIPCSem = aMachine->mIPCSem;
7702#endif
7703 return true;
7704 }
7705
7706 return false;
7707}
7708
7709/**
7710 * Returns @c true if the given machine has an spawning direct session and
7711 * returns and additional session data (on some platforms) if so.
7712 *
7713 * Note that when the method returns @c false, the arguments remain unchanged.
7714 *
7715 * @param aPID PID of the spawned direct session process.
7716 *
7717 * @note locks this object for reading.
7718 */
7719#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7720bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)
7721#else
7722bool Machine::isSessionSpawning()
7723#endif
7724{
7725 AutoLimitedCaller autoCaller(this);
7726 AssertComRCReturn(autoCaller.rc(), false);
7727
7728 /* just return false for inaccessible machines */
7729 if (autoCaller.state() != Ready)
7730 return false;
7731
7732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7733
7734 if (mData->mSession.mState == SessionState_Spawning)
7735 {
7736#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7737 /* Additional session data */
7738 if (aPID != NULL)
7739 {
7740 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);
7741 *aPID = mData->mSession.mPID;
7742 }
7743#endif
7744 return true;
7745 }
7746
7747 return false;
7748}
7749
7750/**
7751 * Called from the client watcher thread to check for unexpected client process
7752 * death during Session_Spawning state (e.g. before it successfully opened a
7753 * direct session).
7754 *
7755 * On Win32 and on OS/2, this method is called only when we've got the
7756 * direct client's process termination notification, so it always returns @c
7757 * true.
7758 *
7759 * On other platforms, this method returns @c true if the client process is
7760 * terminated and @c false if it's still alive.
7761 *
7762 * @note Locks this object for writing.
7763 */
7764bool Machine::checkForSpawnFailure()
7765{
7766 AutoCaller autoCaller(this);
7767 if (!autoCaller.isOk())
7768 {
7769 /* nothing to do */
7770 LogFlowThisFunc(("Already uninitialized!\n"));
7771 return true;
7772 }
7773
7774 /* VirtualBox::addProcessToReap() needs a write lock */
7775 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
7776
7777 if (mData->mSession.mState != SessionState_Spawning)
7778 {
7779 /* nothing to do */
7780 LogFlowThisFunc(("Not spawning any more!\n"));
7781 return true;
7782 }
7783
7784 HRESULT rc = S_OK;
7785
7786#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
7787
7788 /* the process was already unexpectedly terminated, we just need to set an
7789 * error and finalize session spawning */
7790 rc = setError(E_FAIL,
7791 tr("The virtual machine '%s' has terminated unexpectedly during startup"),
7792 getName().c_str());
7793#else
7794
7795 /* PID not yet initialized, skip check. */
7796 if (mData->mSession.mPID == NIL_RTPROCESS)
7797 return false;
7798
7799 RTPROCSTATUS status;
7800 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK,
7801 &status);
7802
7803 if (vrc != VERR_PROCESS_RUNNING)
7804 {
7805 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7806 rc = setError(E_FAIL,
7807 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"),
7808 getName().c_str(), status.iStatus);
7809 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7810 rc = setError(E_FAIL,
7811 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"),
7812 getName().c_str(), status.iStatus);
7813 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7814 rc = setError(E_FAIL,
7815 tr("The virtual machine '%s' has terminated abnormally"),
7816 getName().c_str(), status.iStatus);
7817 else
7818 rc = setError(E_FAIL,
7819 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"),
7820 getName().c_str(), rc);
7821 }
7822
7823#endif
7824
7825 if (FAILED(rc))
7826 {
7827 /* Close the remote session, remove the remote control from the list
7828 * and reset session state to Closed (@note keep the code in sync with
7829 * the relevant part in checkForSpawnFailure()). */
7830
7831 Assert(mData->mSession.mRemoteControls.size() == 1);
7832 if (mData->mSession.mRemoteControls.size() == 1)
7833 {
7834 ErrorInfoKeeper eik;
7835 mData->mSession.mRemoteControls.front()->Uninitialize();
7836 }
7837
7838 mData->mSession.mRemoteControls.clear();
7839 mData->mSession.mState = SessionState_Unlocked;
7840
7841 /* finalize the progress after setting the state */
7842 if (!mData->mSession.mProgress.isNull())
7843 {
7844 mData->mSession.mProgress->notifyComplete(rc);
7845 mData->mSession.mProgress.setNull();
7846 }
7847
7848 mParent->addProcessToReap(mData->mSession.mPID);
7849 mData->mSession.mPID = NIL_RTPROCESS;
7850
7851 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7852 return true;
7853 }
7854
7855 return false;
7856}
7857
7858/**
7859 * Checks whether the machine can be registered. If so, commits and saves
7860 * all settings.
7861 *
7862 * @note Must be called from mParent's write lock. Locks this object and
7863 * children for writing.
7864 */
7865HRESULT Machine::prepareRegister()
7866{
7867 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7868
7869 AutoLimitedCaller autoCaller(this);
7870 AssertComRCReturnRC(autoCaller.rc());
7871
7872 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7873
7874 /* wait for state dependents to drop to zero */
7875 ensureNoStateDependencies();
7876
7877 if (!mData->mAccessible)
7878 return setError(VBOX_E_INVALID_OBJECT_STATE,
7879 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7880 mUserData->s.strName.c_str(),
7881 mData->mUuid.toString().c_str());
7882
7883 AssertReturn(autoCaller.state() == Ready, E_FAIL);
7884
7885 if (mData->mRegistered)
7886 return setError(VBOX_E_INVALID_OBJECT_STATE,
7887 tr("The machine '%s' with UUID {%s} is already registered"),
7888 mUserData->s.strName.c_str(),
7889 mData->mUuid.toString().c_str());
7890
7891 HRESULT rc = S_OK;
7892
7893 // Ensure the settings are saved. If we are going to be registered and
7894 // no config file exists yet, create it by calling saveSettings() too.
7895 if ( (mData->flModifications)
7896 || (!mData->pMachineConfigFile->fileExists())
7897 )
7898 {
7899 rc = saveSettings(NULL);
7900 // no need to check whether VirtualBox.xml needs saving too since
7901 // we can't have a machine XML file rename pending
7902 if (FAILED(rc)) return rc;
7903 }
7904
7905 /* more config checking goes here */
7906
7907 if (SUCCEEDED(rc))
7908 {
7909 /* we may have had implicit modifications we want to fix on success */
7910 commit();
7911
7912 mData->mRegistered = true;
7913 }
7914 else
7915 {
7916 /* we may have had implicit modifications we want to cancel on failure*/
7917 rollback(false /* aNotify */);
7918 }
7919
7920 return rc;
7921}
7922
7923/**
7924 * Increases the number of objects dependent on the machine state or on the
7925 * registered state. Guarantees that these two states will not change at least
7926 * until #releaseStateDependency() is called.
7927 *
7928 * Depending on the @a aDepType value, additional state checks may be made.
7929 * These checks will set extended error info on failure. See
7930 * #checkStateDependency() for more info.
7931 *
7932 * If this method returns a failure, the dependency is not added and the caller
7933 * is not allowed to rely on any particular machine state or registration state
7934 * value and may return the failed result code to the upper level.
7935 *
7936 * @param aDepType Dependency type to add.
7937 * @param aState Current machine state (NULL if not interested).
7938 * @param aRegistered Current registered state (NULL if not interested).
7939 *
7940 * @note Locks this object for writing.
7941 */
7942HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7943 MachineState_T *aState /* = NULL */,
7944 BOOL *aRegistered /* = NULL */)
7945{
7946 AutoCaller autoCaller(this);
7947 AssertComRCReturnRC(autoCaller.rc());
7948
7949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7950
7951 HRESULT rc = checkStateDependency(aDepType);
7952 if (FAILED(rc)) return rc;
7953
7954 {
7955 if (mData->mMachineStateChangePending != 0)
7956 {
7957 /* ensureNoStateDependencies() is waiting for state dependencies to
7958 * drop to zero so don't add more. It may make sense to wait a bit
7959 * and retry before reporting an error (since the pending state
7960 * transition should be really quick) but let's just assert for
7961 * now to see if it ever happens on practice. */
7962
7963 AssertFailed();
7964
7965 return setError(E_ACCESSDENIED,
7966 tr("Machine state change is in progress. Please retry the operation later."));
7967 }
7968
7969 ++mData->mMachineStateDeps;
7970 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7971 }
7972
7973 if (aState)
7974 *aState = mData->mMachineState;
7975 if (aRegistered)
7976 *aRegistered = mData->mRegistered;
7977
7978 return S_OK;
7979}
7980
7981/**
7982 * Decreases the number of objects dependent on the machine state.
7983 * Must always complete the #addStateDependency() call after the state
7984 * dependency is no more necessary.
7985 */
7986void Machine::releaseStateDependency()
7987{
7988 AutoCaller autoCaller(this);
7989 AssertComRCReturnVoid(autoCaller.rc());
7990
7991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7992
7993 /* releaseStateDependency() w/o addStateDependency()? */
7994 AssertReturnVoid(mData->mMachineStateDeps != 0);
7995 -- mData->mMachineStateDeps;
7996
7997 if (mData->mMachineStateDeps == 0)
7998 {
7999 /* inform ensureNoStateDependencies() that there are no more deps */
8000 if (mData->mMachineStateChangePending != 0)
8001 {
8002 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8003 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8004 }
8005 }
8006}
8007
8008Utf8Str Machine::getExtraData(const Utf8Str &strKey)
8009{
8010 /* start with nothing found */
8011 Utf8Str strResult("");
8012
8013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8014
8015 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8016 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8017 // found:
8018 strResult = it->second; // source is a Utf8Str
8019
8020 return strResult;
8021}
8022
8023// protected methods
8024/////////////////////////////////////////////////////////////////////////////
8025
8026/**
8027 * Performs machine state checks based on the @a aDepType value. If a check
8028 * fails, this method will set extended error info, otherwise it will return
8029 * S_OK. It is supposed, that on failure, the caller will immediately return
8030 * the return value of this method to the upper level.
8031 *
8032 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8033 *
8034 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8035 * current state of this machine object allows to change settings of the
8036 * machine (i.e. the machine is not registered, or registered but not running
8037 * and not saved). It is useful to call this method from Machine setters
8038 * before performing any change.
8039 *
8040 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8041 * as for MutableStateDep except that if the machine is saved, S_OK is also
8042 * returned. This is useful in setters which allow changing machine
8043 * properties when it is in the saved state.
8044 *
8045 * When @a aDepType is OfflineStateDep, this method returns S_OK if the
8046 * state is one of the 4 offline states (PoweredOff, Saved, Teleported,
8047 * Aborted).
8048 *
8049 * @param aDepType Dependency type to check.
8050 *
8051 * @note Non Machine based classes should use #addStateDependency() and
8052 * #releaseStateDependency() methods or the smart AutoStateDependency
8053 * template.
8054 *
8055 * @note This method must be called from under this object's read or write
8056 * lock.
8057 */
8058HRESULT Machine::checkStateDependency(StateDependency aDepType)
8059{
8060 switch (aDepType)
8061 {
8062 case AnyStateDep:
8063 {
8064 break;
8065 }
8066 case MutableStateDep:
8067 {
8068 if ( mData->mRegistered
8069 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8070 || ( mData->mMachineState != MachineState_Paused
8071 && mData->mMachineState != MachineState_Running
8072 && mData->mMachineState != MachineState_Aborted
8073 && mData->mMachineState != MachineState_Teleported
8074 && mData->mMachineState != MachineState_PoweredOff
8075 )
8076 )
8077 )
8078 return setError(VBOX_E_INVALID_VM_STATE,
8079 tr("The machine is not mutable (state is %s)"),
8080 Global::stringifyMachineState(mData->mMachineState));
8081 break;
8082 }
8083 case MutableOrSavedStateDep:
8084 {
8085 if ( mData->mRegistered
8086 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */
8087 || ( mData->mMachineState != MachineState_Paused
8088 && mData->mMachineState != MachineState_Running
8089 && mData->mMachineState != MachineState_Aborted
8090 && mData->mMachineState != MachineState_Teleported
8091 && mData->mMachineState != MachineState_Saved
8092 && mData->mMachineState != MachineState_PoweredOff
8093 )
8094 )
8095 )
8096 return setError(VBOX_E_INVALID_VM_STATE,
8097 tr("The machine is not mutable (state is %s)"),
8098 Global::stringifyMachineState(mData->mMachineState));
8099 break;
8100 }
8101 case OfflineStateDep:
8102 {
8103 if ( mData->mRegistered
8104 && ( !isSessionMachine()
8105 || ( mData->mMachineState != MachineState_PoweredOff
8106 && mData->mMachineState != MachineState_Saved
8107 && mData->mMachineState != MachineState_Aborted
8108 && mData->mMachineState != MachineState_Teleported
8109 )
8110 )
8111 )
8112 return setError(VBOX_E_INVALID_VM_STATE,
8113 tr("The machine is not offline (state is %s)"),
8114 Global::stringifyMachineState(mData->mMachineState));
8115 break;
8116 }
8117 }
8118
8119 return S_OK;
8120}
8121
8122/**
8123 * Helper to initialize all associated child objects and allocate data
8124 * structures.
8125 *
8126 * This method must be called as a part of the object's initialization procedure
8127 * (usually done in the #init() method).
8128 *
8129 * @note Must be called only from #init() or from #registeredInit().
8130 */
8131HRESULT Machine::initDataAndChildObjects()
8132{
8133 AutoCaller autoCaller(this);
8134 AssertComRCReturnRC(autoCaller.rc());
8135 AssertComRCReturn(autoCaller.state() == InInit ||
8136 autoCaller.state() == Limited, E_FAIL);
8137
8138 AssertReturn(!mData->mAccessible, E_FAIL);
8139
8140 /* allocate data structures */
8141 mSSData.allocate();
8142 mUserData.allocate();
8143 mHWData.allocate();
8144 mMediaData.allocate();
8145 mStorageControllers.allocate();
8146
8147 /* initialize mOSTypeId */
8148 mUserData->s.strOsType = mParent->getUnknownOSType()->id();
8149
8150 /* create associated BIOS settings object */
8151 unconst(mBIOSSettings).createObject();
8152 mBIOSSettings->init(this);
8153
8154 /* create an associated VRDE object (default is disabled) */
8155 unconst(mVRDEServer).createObject();
8156 mVRDEServer->init(this);
8157
8158 /* create associated serial port objects */
8159 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8160 {
8161 unconst(mSerialPorts[slot]).createObject();
8162 mSerialPorts[slot]->init(this, slot);
8163 }
8164
8165 /* create associated parallel port objects */
8166 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8167 {
8168 unconst(mParallelPorts[slot]).createObject();
8169 mParallelPorts[slot]->init(this, slot);
8170 }
8171
8172 /* create the audio adapter object (always present, default is disabled) */
8173 unconst(mAudioAdapter).createObject();
8174 mAudioAdapter->init(this);
8175
8176 /* create the USB controller object (always present, default is disabled) */
8177 unconst(mUSBController).createObject();
8178 mUSBController->init(this);
8179
8180 /* create associated network adapter objects */
8181 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8182 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8183 {
8184 unconst(mNetworkAdapters[slot]).createObject();
8185 mNetworkAdapters[slot]->init(this, slot);
8186 }
8187
8188 /* create the bandwidth control */
8189 unconst(mBandwidthControl).createObject();
8190 mBandwidthControl->init(this);
8191
8192 return S_OK;
8193}
8194
8195/**
8196 * Helper to uninitialize all associated child objects and to free all data
8197 * structures.
8198 *
8199 * This method must be called as a part of the object's uninitialization
8200 * procedure (usually done in the #uninit() method).
8201 *
8202 * @note Must be called only from #uninit() or from #registeredInit().
8203 */
8204void Machine::uninitDataAndChildObjects()
8205{
8206 AutoCaller autoCaller(this);
8207 AssertComRCReturnVoid(autoCaller.rc());
8208 AssertComRCReturnVoid( autoCaller.state() == InUninit
8209 || autoCaller.state() == Limited);
8210
8211 /* tell all our other child objects we've been uninitialized */
8212 if (mBandwidthControl)
8213 {
8214 mBandwidthControl->uninit();
8215 unconst(mBandwidthControl).setNull();
8216 }
8217
8218 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
8219 {
8220 if (mNetworkAdapters[slot])
8221 {
8222 mNetworkAdapters[slot]->uninit();
8223 unconst(mNetworkAdapters[slot]).setNull();
8224 }
8225 }
8226
8227 if (mUSBController)
8228 {
8229 mUSBController->uninit();
8230 unconst(mUSBController).setNull();
8231 }
8232
8233 if (mAudioAdapter)
8234 {
8235 mAudioAdapter->uninit();
8236 unconst(mAudioAdapter).setNull();
8237 }
8238
8239 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
8240 {
8241 if (mParallelPorts[slot])
8242 {
8243 mParallelPorts[slot]->uninit();
8244 unconst(mParallelPorts[slot]).setNull();
8245 }
8246 }
8247
8248 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
8249 {
8250 if (mSerialPorts[slot])
8251 {
8252 mSerialPorts[slot]->uninit();
8253 unconst(mSerialPorts[slot]).setNull();
8254 }
8255 }
8256
8257 if (mVRDEServer)
8258 {
8259 mVRDEServer->uninit();
8260 unconst(mVRDEServer).setNull();
8261 }
8262
8263 if (mBIOSSettings)
8264 {
8265 mBIOSSettings->uninit();
8266 unconst(mBIOSSettings).setNull();
8267 }
8268
8269 /* Deassociate media (only when a real Machine or a SnapshotMachine
8270 * instance is uninitialized; SessionMachine instances refer to real
8271 * Machine media). This is necessary for a clean re-initialization of
8272 * the VM after successfully re-checking the accessibility state. Note
8273 * that in case of normal Machine or SnapshotMachine uninitialization (as
8274 * a result of unregistering or deleting the snapshot), outdated media
8275 * attachments will already be uninitialized and deleted, so this
8276 * code will not affect them. */
8277 if ( !!mMediaData
8278 && (!isSessionMachine())
8279 )
8280 {
8281 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8282 it != mMediaData->mAttachments.end();
8283 ++it)
8284 {
8285 ComObjPtr<Medium> pMedium = (*it)->getMedium();
8286 if (pMedium.isNull())
8287 continue;
8288 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId());
8289 AssertComRC(rc);
8290 }
8291 }
8292
8293 if (!isSessionMachine() && !isSnapshotMachine())
8294 {
8295 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8296 if (mData->mFirstSnapshot)
8297 {
8298 // snapshots tree is protected by machine write lock; strictly
8299 // this isn't necessary here since we're deleting the entire
8300 // machine, but otherwise we assert in Snapshot::uninit()
8301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8302 mData->mFirstSnapshot->uninit();
8303 mData->mFirstSnapshot.setNull();
8304 }
8305
8306 mData->mCurrentSnapshot.setNull();
8307 }
8308
8309 /* free data structures (the essential mData structure is not freed here
8310 * since it may be still in use) */
8311 mMediaData.free();
8312 mStorageControllers.free();
8313 mHWData.free();
8314 mUserData.free();
8315 mSSData.free();
8316}
8317
8318/**
8319 * Returns a pointer to the Machine object for this machine that acts like a
8320 * parent for complex machine data objects such as shared folders, etc.
8321 *
8322 * For primary Machine objects and for SnapshotMachine objects, returns this
8323 * object's pointer itself. For SessionMachine objects, returns the peer
8324 * (primary) machine pointer.
8325 */
8326Machine* Machine::getMachine()
8327{
8328 if (isSessionMachine())
8329 return (Machine*)mPeer;
8330 return this;
8331}
8332
8333/**
8334 * Makes sure that there are no machine state dependents. If necessary, waits
8335 * for the number of dependents to drop to zero.
8336 *
8337 * Make sure this method is called from under this object's write lock to
8338 * guarantee that no new dependents may be added when this method returns
8339 * control to the caller.
8340 *
8341 * @note Locks this object for writing. The lock will be released while waiting
8342 * (if necessary).
8343 *
8344 * @warning To be used only in methods that change the machine state!
8345 */
8346void Machine::ensureNoStateDependencies()
8347{
8348 AssertReturnVoid(isWriteLockOnCurrentThread());
8349
8350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8351
8352 /* Wait for all state dependents if necessary */
8353 if (mData->mMachineStateDeps != 0)
8354 {
8355 /* lazy semaphore creation */
8356 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8357 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8358
8359 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8360 mData->mMachineStateDeps));
8361
8362 ++mData->mMachineStateChangePending;
8363
8364 /* reset the semaphore before waiting, the last dependent will signal
8365 * it */
8366 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8367
8368 alock.release();
8369
8370 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8371
8372 alock.acquire();
8373
8374 -- mData->mMachineStateChangePending;
8375 }
8376}
8377
8378/**
8379 * Changes the machine state and informs callbacks.
8380 *
8381 * This method is not intended to fail so it either returns S_OK or asserts (and
8382 * returns a failure).
8383 *
8384 * @note Locks this object for writing.
8385 */
8386HRESULT Machine::setMachineState(MachineState_T aMachineState)
8387{
8388 LogFlowThisFuncEnter();
8389 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8390
8391 AutoCaller autoCaller(this);
8392 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8393
8394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8395
8396 /* wait for state dependents to drop to zero */
8397 ensureNoStateDependencies();
8398
8399 if (mData->mMachineState != aMachineState)
8400 {
8401 mData->mMachineState = aMachineState;
8402
8403 RTTimeNow(&mData->mLastStateChange);
8404
8405 mParent->onMachineStateChange(mData->mUuid, aMachineState);
8406 }
8407
8408 LogFlowThisFuncLeave();
8409 return S_OK;
8410}
8411
8412/**
8413 * Searches for a shared folder with the given logical name
8414 * in the collection of shared folders.
8415 *
8416 * @param aName logical name of the shared folder
8417 * @param aSharedFolder where to return the found object
8418 * @param aSetError whether to set the error info if the folder is
8419 * not found
8420 * @return
8421 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8422 *
8423 * @note
8424 * must be called from under the object's lock!
8425 */
8426HRESULT Machine::findSharedFolder(const Utf8Str &aName,
8427 ComObjPtr<SharedFolder> &aSharedFolder,
8428 bool aSetError /* = false */)
8429{
8430 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8431 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8432 it != mHWData->mSharedFolders.end();
8433 ++it)
8434 {
8435 SharedFolder *pSF = *it;
8436 AutoCaller autoCaller(pSF);
8437 if (pSF->getName() == aName)
8438 {
8439 aSharedFolder = pSF;
8440 rc = S_OK;
8441 break;
8442 }
8443 }
8444
8445 if (aSetError && FAILED(rc))
8446 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8447
8448 return rc;
8449}
8450
8451/**
8452 * Initializes all machine instance data from the given settings structures
8453 * from XML. The exception is the machine UUID which needs special handling
8454 * depending on the caller's use case, so the caller needs to set that herself.
8455 *
8456 * This gets called in several contexts during machine initialization:
8457 *
8458 * -- When machine XML exists on disk already and needs to be loaded into memory,
8459 * for example, from registeredInit() to load all registered machines on
8460 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8461 * attached to the machine should be part of some media registry already.
8462 *
8463 * -- During OVF import, when a machine config has been constructed from an
8464 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8465 * ensure that the media listed as attachments in the config (which have
8466 * been imported from the OVF) receive the correct registry ID.
8467 *
8468 * -- During VM cloning.
8469 *
8470 * @param config Machine settings from XML.
8471 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
8472 * @return
8473 */
8474HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8475 const Guid *puuidRegistry)
8476{
8477 // copy name, description, OS type, teleporter, UTC etc.
8478 mUserData->s = config.machineUserData;
8479
8480 // look up the object by Id to check it is valid
8481 ComPtr<IGuestOSType> guestOSType;
8482 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8483 guestOSType.asOutParam());
8484 if (FAILED(rc)) return rc;
8485
8486 // stateFile (optional)
8487 if (config.strStateFile.isEmpty())
8488 mSSData->strStateFilePath.setNull();
8489 else
8490 {
8491 Utf8Str stateFilePathFull(config.strStateFile);
8492 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
8493 if (RT_FAILURE(vrc))
8494 return setError(E_FAIL,
8495 tr("Invalid saved state file path '%s' (%Rrc)"),
8496 config.strStateFile.c_str(),
8497 vrc);
8498 mSSData->strStateFilePath = stateFilePathFull;
8499 }
8500
8501 // snapshot folder needs special processing so set it again
8502 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8503 if (FAILED(rc)) return rc;
8504
8505 /* Copy the extra data items (Not in any case config is already the same as
8506 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8507 * make sure the extra data map is copied). */
8508 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8509
8510 /* currentStateModified (optional, default is true) */
8511 mData->mCurrentStateModified = config.fCurrentStateModified;
8512
8513 mData->mLastStateChange = config.timeLastStateChange;
8514
8515 /*
8516 * note: all mUserData members must be assigned prior this point because
8517 * we need to commit changes in order to let mUserData be shared by all
8518 * snapshot machine instances.
8519 */
8520 mUserData.commitCopy();
8521
8522 // machine registry, if present (must be loaded before snapshots)
8523 if (config.canHaveOwnMediaRegistry())
8524 {
8525 // determine machine folder
8526 Utf8Str strMachineFolder = getSettingsFileFull();
8527 strMachineFolder.stripFilename();
8528 rc = mParent->initMedia(getId(), // media registry ID == machine UUID
8529 config.mediaRegistry,
8530 strMachineFolder);
8531 if (FAILED(rc)) return rc;
8532 }
8533
8534 /* Snapshot node (optional) */
8535 size_t cRootSnapshots;
8536 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8537 {
8538 // there must be only one root snapshot
8539 Assert(cRootSnapshots == 1);
8540
8541 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8542
8543 rc = loadSnapshot(snap,
8544 config.uuidCurrentSnapshot,
8545 NULL); // no parent == first snapshot
8546 if (FAILED(rc)) return rc;
8547 }
8548
8549 // hardware data
8550 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8551 if (FAILED(rc)) return rc;
8552
8553 // load storage controllers
8554 rc = loadStorageControllers(config.storageMachine,
8555 puuidRegistry,
8556 NULL /* puuidSnapshot */);
8557 if (FAILED(rc)) return rc;
8558
8559 /*
8560 * NOTE: the assignment below must be the last thing to do,
8561 * otherwise it will be not possible to change the settings
8562 * somewhere in the code above because all setters will be
8563 * blocked by checkStateDependency(MutableStateDep).
8564 */
8565
8566 /* set the machine state to Aborted or Saved when appropriate */
8567 if (config.fAborted)
8568 {
8569 mSSData->strStateFilePath.setNull();
8570
8571 /* no need to use setMachineState() during init() */
8572 mData->mMachineState = MachineState_Aborted;
8573 }
8574 else if (!mSSData->strStateFilePath.isEmpty())
8575 {
8576 /* no need to use setMachineState() during init() */
8577 mData->mMachineState = MachineState_Saved;
8578 }
8579
8580 // after loading settings, we are no longer different from the XML on disk
8581 mData->flModifications = 0;
8582
8583 return S_OK;
8584}
8585
8586/**
8587 * Recursively loads all snapshots starting from the given.
8588 *
8589 * @param aNode <Snapshot> node.
8590 * @param aCurSnapshotId Current snapshot ID from the settings file.
8591 * @param aParentSnapshot Parent snapshot.
8592 */
8593HRESULT Machine::loadSnapshot(const settings::Snapshot &data,
8594 const Guid &aCurSnapshotId,
8595 Snapshot *aParentSnapshot)
8596{
8597 AssertReturn(!isSnapshotMachine(), E_FAIL);
8598 AssertReturn(!isSessionMachine(), E_FAIL);
8599
8600 HRESULT rc = S_OK;
8601
8602 Utf8Str strStateFile;
8603 if (!data.strStateFile.isEmpty())
8604 {
8605 /* optional */
8606 strStateFile = data.strStateFile;
8607 int vrc = calculateFullPath(strStateFile, strStateFile);
8608 if (RT_FAILURE(vrc))
8609 return setError(E_FAIL,
8610 tr("Invalid saved state file path '%s' (%Rrc)"),
8611 strStateFile.c_str(),
8612 vrc);
8613 }
8614
8615 /* create a snapshot machine object */
8616 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8617 pSnapshotMachine.createObject();
8618 rc = pSnapshotMachine->initFromSettings(this,
8619 data.hardware,
8620 &data.debugging,
8621 &data.autostart,
8622 data.storage,
8623 data.uuid.ref(),
8624 strStateFile);
8625 if (FAILED(rc)) return rc;
8626
8627 /* create a snapshot object */
8628 ComObjPtr<Snapshot> pSnapshot;
8629 pSnapshot.createObject();
8630 /* initialize the snapshot */
8631 rc = pSnapshot->init(mParent, // VirtualBox object
8632 data.uuid,
8633 data.strName,
8634 data.strDescription,
8635 data.timestamp,
8636 pSnapshotMachine,
8637 aParentSnapshot);
8638 if (FAILED(rc)) return rc;
8639
8640 /* memorize the first snapshot if necessary */
8641 if (!mData->mFirstSnapshot)
8642 mData->mFirstSnapshot = pSnapshot;
8643
8644 /* memorize the current snapshot when appropriate */
8645 if ( !mData->mCurrentSnapshot
8646 && pSnapshot->getId() == aCurSnapshotId
8647 )
8648 mData->mCurrentSnapshot = pSnapshot;
8649
8650 // now create the children
8651 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8652 it != data.llChildSnapshots.end();
8653 ++it)
8654 {
8655 const settings::Snapshot &childData = *it;
8656 // recurse
8657 rc = loadSnapshot(childData,
8658 aCurSnapshotId,
8659 pSnapshot); // parent = the one we created above
8660 if (FAILED(rc)) return rc;
8661 }
8662
8663 return rc;
8664}
8665
8666/**
8667 * Loads settings into mHWData.
8668 *
8669 * @param data Reference to the hardware settings.
8670 * @param pDbg Pointer to the debugging settings.
8671 * @param pAutostart Pointer to the autostart settings.
8672 */
8673HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8674 const settings::Autostart *pAutostart)
8675{
8676 AssertReturn(!isSessionMachine(), E_FAIL);
8677
8678 HRESULT rc = S_OK;
8679
8680 try
8681 {
8682 /* The hardware version attribute (optional). */
8683 mHWData->mHWVersion = data.strVersion;
8684 mHWData->mHardwareUUID = data.uuid;
8685
8686 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8687 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive;
8688 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8689 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8690 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8691 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8692 mHWData->mPAEEnabled = data.fPAE;
8693 mHWData->mSyntheticCpu = data.fSyntheticCpu;
8694 mHWData->mLongMode = data.enmLongMode;
8695 mHWData->mCPUCount = data.cCPUs;
8696 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8697 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8698
8699 // cpu
8700 if (mHWData->mCPUHotPlugEnabled)
8701 {
8702 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8703 it != data.llCpus.end();
8704 ++it)
8705 {
8706 const settings::Cpu &cpu = *it;
8707
8708 mHWData->mCPUAttached[cpu.ulId] = true;
8709 }
8710 }
8711
8712 // cpuid leafs
8713 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8714 it != data.llCpuIdLeafs.end();
8715 ++it)
8716 {
8717 const settings::CpuIdLeaf &leaf = *it;
8718
8719 switch (leaf.ulId)
8720 {
8721 case 0x0:
8722 case 0x1:
8723 case 0x2:
8724 case 0x3:
8725 case 0x4:
8726 case 0x5:
8727 case 0x6:
8728 case 0x7:
8729 case 0x8:
8730 case 0x9:
8731 case 0xA:
8732 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8733 break;
8734
8735 case 0x80000000:
8736 case 0x80000001:
8737 case 0x80000002:
8738 case 0x80000003:
8739 case 0x80000004:
8740 case 0x80000005:
8741 case 0x80000006:
8742 case 0x80000007:
8743 case 0x80000008:
8744 case 0x80000009:
8745 case 0x8000000A:
8746 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8747 break;
8748
8749 default:
8750 /* just ignore */
8751 break;
8752 }
8753 }
8754
8755 mHWData->mMemorySize = data.ulMemorySizeMB;
8756 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8757
8758 // boot order
8759 for (size_t i = 0;
8760 i < RT_ELEMENTS(mHWData->mBootOrder);
8761 i++)
8762 {
8763 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8764 if (it == data.mapBootOrder.end())
8765 mHWData->mBootOrder[i] = DeviceType_Null;
8766 else
8767 mHWData->mBootOrder[i] = it->second;
8768 }
8769
8770 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8771 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8772 mHWData->mMonitorCount = data.cMonitors;
8773 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8774 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8775 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8776 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8777 mHWData->mVideoCaptureEnabled = false; /* @todo r=klaus restore to data.fVideoCaptureEnabled */
8778 mHWData->mVideoCaptureFile = data.strVideoCaptureFile;
8779 mHWData->mFirmwareType = data.firmwareType;
8780 mHWData->mPointingHIDType = data.pointingHIDType;
8781 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8782 mHWData->mChipsetType = data.chipsetType;
8783 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam;
8784 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8785 mHWData->mHPETEnabled = data.fHPETEnabled;
8786
8787 /* VRDEServer */
8788 rc = mVRDEServer->loadSettings(data.vrdeSettings);
8789 if (FAILED(rc)) return rc;
8790
8791 /* BIOS */
8792 rc = mBIOSSettings->loadSettings(data.biosSettings);
8793 if (FAILED(rc)) return rc;
8794
8795 // Bandwidth control (must come before network adapters)
8796 rc = mBandwidthControl->loadSettings(data.ioSettings);
8797 if (FAILED(rc)) return rc;
8798
8799 /* USB Controller */
8800 rc = mUSBController->loadSettings(data.usbController);
8801 if (FAILED(rc)) return rc;
8802
8803 // network adapters
8804 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8805 uint32_t oldCount = mNetworkAdapters.size();
8806 if (newCount > oldCount)
8807 {
8808 mNetworkAdapters.resize(newCount);
8809 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++)
8810 {
8811 unconst(mNetworkAdapters[slot]).createObject();
8812 mNetworkAdapters[slot]->init(this, slot);
8813 }
8814 }
8815 else if (newCount < oldCount)
8816 mNetworkAdapters.resize(newCount);
8817 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8818 it != data.llNetworkAdapters.end();
8819 ++it)
8820 {
8821 const settings::NetworkAdapter &nic = *it;
8822
8823 /* slot unicity is guaranteed by XML Schema */
8824 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8825 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
8826 if (FAILED(rc)) return rc;
8827 }
8828
8829 // serial ports
8830 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8831 it != data.llSerialPorts.end();
8832 ++it)
8833 {
8834 const settings::SerialPort &s = *it;
8835
8836 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8837 rc = mSerialPorts[s.ulSlot]->loadSettings(s);
8838 if (FAILED(rc)) return rc;
8839 }
8840
8841 // parallel ports (optional)
8842 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8843 it != data.llParallelPorts.end();
8844 ++it)
8845 {
8846 const settings::ParallelPort &p = *it;
8847
8848 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8849 rc = mParallelPorts[p.ulSlot]->loadSettings(p);
8850 if (FAILED(rc)) return rc;
8851 }
8852
8853 /* AudioAdapter */
8854 rc = mAudioAdapter->loadSettings(data.audioAdapter);
8855 if (FAILED(rc)) return rc;
8856
8857 /* Shared folders */
8858 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8859 it != data.llSharedFolders.end();
8860 ++it)
8861 {
8862 const settings::SharedFolder &sf = *it;
8863
8864 ComObjPtr<SharedFolder> sharedFolder;
8865 /* Check for double entries. Not allowed! */
8866 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8867 if (SUCCEEDED(rc))
8868 return setError(VBOX_E_OBJECT_IN_USE,
8869 tr("Shared folder named '%s' already exists"),
8870 sf.strName.c_str());
8871
8872 /* Create the new shared folder. Don't break on error. This will be
8873 * reported when the machine starts. */
8874 sharedFolder.createObject();
8875 rc = sharedFolder->init(getMachine(),
8876 sf.strName,
8877 sf.strHostPath,
8878 RT_BOOL(sf.fWritable),
8879 RT_BOOL(sf.fAutoMount),
8880 false /* fFailOnError */);
8881 if (FAILED(rc)) return rc;
8882 mHWData->mSharedFolders.push_back(sharedFolder);
8883 }
8884
8885 // Clipboard
8886 mHWData->mClipboardMode = data.clipboardMode;
8887
8888 // drag'n'drop
8889 mHWData->mDragAndDropMode = data.dragAndDropMode;
8890
8891 // guest settings
8892 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8893
8894 // IO settings
8895 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8896 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8897
8898 // Host PCI devices
8899 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8900 it != data.pciAttachments.end();
8901 ++it)
8902 {
8903 const settings::HostPCIDeviceAttachment &hpda = *it;
8904 ComObjPtr<PCIDeviceAttachment> pda;
8905
8906 pda.createObject();
8907 pda->loadSettings(this, hpda);
8908 mHWData->mPCIDeviceAssignments.push_back(pda);
8909 }
8910
8911 /*
8912 * (The following isn't really real hardware, but it lives in HWData
8913 * for reasons of convenience.)
8914 */
8915
8916#ifdef VBOX_WITH_GUEST_PROPS
8917 /* Guest properties (optional) */
8918 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin();
8919 it != data.llGuestProperties.end();
8920 ++it)
8921 {
8922 const settings::GuestProperty &prop = *it;
8923 uint32_t fFlags = guestProp::NILFLAG;
8924 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
8925 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8926 mHWData->mGuestProperties[prop.strName] = property;
8927 }
8928
8929 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns;
8930#endif /* VBOX_WITH_GUEST_PROPS defined */
8931
8932 rc = loadDebugging(pDbg);
8933 if (FAILED(rc))
8934 return rc;
8935
8936 mHWData->mAutostart = *pAutostart;
8937
8938 /* default frontend */
8939 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8940 }
8941 catch(std::bad_alloc &)
8942 {
8943 return E_OUTOFMEMORY;
8944 }
8945
8946 AssertComRC(rc);
8947 return rc;
8948}
8949
8950/**
8951 * Called from Machine::loadHardware() to load the debugging settings of the
8952 * machine.
8953 *
8954 * @param pDbg Pointer to the settings.
8955 */
8956HRESULT Machine::loadDebugging(const settings::Debugging *pDbg)
8957{
8958 mHWData->mDebugging = *pDbg;
8959 /* no more processing currently required, this will probably change. */
8960 return S_OK;
8961}
8962
8963/**
8964 * Called from loadMachineDataFromSettings() for the storage controller data, including media.
8965 *
8966 * @param data
8967 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
8968 * @param puuidSnapshot
8969 * @return
8970 */
8971HRESULT Machine::loadStorageControllers(const settings::Storage &data,
8972 const Guid *puuidRegistry,
8973 const Guid *puuidSnapshot)
8974{
8975 AssertReturn(!isSessionMachine(), E_FAIL);
8976
8977 HRESULT rc = S_OK;
8978
8979 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
8980 it != data.llStorageControllers.end();
8981 ++it)
8982 {
8983 const settings::StorageController &ctlData = *it;
8984
8985 ComObjPtr<StorageController> pCtl;
8986 /* Try to find one with the name first. */
8987 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
8988 if (SUCCEEDED(rc))
8989 return setError(VBOX_E_OBJECT_IN_USE,
8990 tr("Storage controller named '%s' already exists"),
8991 ctlData.strName.c_str());
8992
8993 pCtl.createObject();
8994 rc = pCtl->init(this,
8995 ctlData.strName,
8996 ctlData.storageBus,
8997 ctlData.ulInstance,
8998 ctlData.fBootable);
8999 if (FAILED(rc)) return rc;
9000
9001 mStorageControllers->push_back(pCtl);
9002
9003 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9004 if (FAILED(rc)) return rc;
9005
9006 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9007 if (FAILED(rc)) return rc;
9008
9009 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9010 if (FAILED(rc)) return rc;
9011
9012 /* Set IDE emulation settings (only for AHCI controller). */
9013 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9014 {
9015 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9016 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9017 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9018 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9019 )
9020 return rc;
9021 }
9022
9023 /* Load the attached devices now. */
9024 rc = loadStorageDevices(pCtl,
9025 ctlData,
9026 puuidRegistry,
9027 puuidSnapshot);
9028 if (FAILED(rc)) return rc;
9029 }
9030
9031 return S_OK;
9032}
9033
9034/**
9035 * Called from loadStorageControllers for a controller's devices.
9036 *
9037 * @param aStorageController
9038 * @param data
9039 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings()
9040 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9041 * @return
9042 */
9043HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
9044 const settings::StorageController &data,
9045 const Guid *puuidRegistry,
9046 const Guid *puuidSnapshot)
9047{
9048 HRESULT rc = S_OK;
9049
9050 /* paranoia: detect duplicate attachments */
9051 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9052 it != data.llAttachedDevices.end();
9053 ++it)
9054 {
9055 const settings::AttachedDevice &ad = *it;
9056
9057 for (settings::AttachedDevicesList::const_iterator it2 = it;
9058 it2 != data.llAttachedDevices.end();
9059 ++it2)
9060 {
9061 if (it == it2)
9062 continue;
9063
9064 const settings::AttachedDevice &ad2 = *it2;
9065
9066 if ( ad.lPort == ad2.lPort
9067 && ad.lDevice == ad2.lDevice)
9068 {
9069 return setError(E_FAIL,
9070 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9071 aStorageController->getName().c_str(),
9072 ad.lPort,
9073 ad.lDevice,
9074 mUserData->s.strName.c_str());
9075 }
9076 }
9077 }
9078
9079 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9080 it != data.llAttachedDevices.end();
9081 ++it)
9082 {
9083 const settings::AttachedDevice &dev = *it;
9084 ComObjPtr<Medium> medium;
9085
9086 switch (dev.deviceType)
9087 {
9088 case DeviceType_Floppy:
9089 case DeviceType_DVD:
9090 if (dev.strHostDriveSrc.isNotEmpty())
9091 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium);
9092 else
9093 rc = mParent->findRemoveableMedium(dev.deviceType,
9094 dev.uuid,
9095 false /* fRefresh */,
9096 false /* aSetError */,
9097 medium);
9098 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9099 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment
9100 rc = S_OK;
9101 break;
9102
9103 case DeviceType_HardDisk:
9104 {
9105 /* find a hard disk by UUID */
9106 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9107 if (FAILED(rc))
9108 {
9109 if (isSnapshotMachine())
9110 {
9111 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9112 // so the user knows that the bad disk is in a snapshot somewhere
9113 com::ErrorInfo info;
9114 return setError(E_FAIL,
9115 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9116 puuidSnapshot->raw(),
9117 info.getText().raw());
9118 }
9119 else
9120 return rc;
9121 }
9122
9123 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9124
9125 if (medium->getType() == MediumType_Immutable)
9126 {
9127 if (isSnapshotMachine())
9128 return setError(E_FAIL,
9129 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9130 "of the virtual machine '%s' ('%s')"),
9131 medium->getLocationFull().c_str(),
9132 dev.uuid.raw(),
9133 puuidSnapshot->raw(),
9134 mUserData->s.strName.c_str(),
9135 mData->m_strConfigFileFull.c_str());
9136
9137 return setError(E_FAIL,
9138 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9139 medium->getLocationFull().c_str(),
9140 dev.uuid.raw(),
9141 mUserData->s.strName.c_str(),
9142 mData->m_strConfigFileFull.c_str());
9143 }
9144
9145 if (medium->getType() == MediumType_MultiAttach)
9146 {
9147 if (isSnapshotMachine())
9148 return setError(E_FAIL,
9149 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9150 "of the virtual machine '%s' ('%s')"),
9151 medium->getLocationFull().c_str(),
9152 dev.uuid.raw(),
9153 puuidSnapshot->raw(),
9154 mUserData->s.strName.c_str(),
9155 mData->m_strConfigFileFull.c_str());
9156
9157 return setError(E_FAIL,
9158 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9159 medium->getLocationFull().c_str(),
9160 dev.uuid.raw(),
9161 mUserData->s.strName.c_str(),
9162 mData->m_strConfigFileFull.c_str());
9163 }
9164
9165 if ( !isSnapshotMachine()
9166 && medium->getChildren().size() != 0
9167 )
9168 return setError(E_FAIL,
9169 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9170 "because it has %d differencing child hard disks"),
9171 medium->getLocationFull().c_str(),
9172 dev.uuid.raw(),
9173 mUserData->s.strName.c_str(),
9174 mData->m_strConfigFileFull.c_str(),
9175 medium->getChildren().size());
9176
9177 if (findAttachment(mMediaData->mAttachments,
9178 medium))
9179 return setError(E_FAIL,
9180 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9181 medium->getLocationFull().c_str(),
9182 dev.uuid.raw(),
9183 mUserData->s.strName.c_str(),
9184 mData->m_strConfigFileFull.c_str());
9185
9186 break;
9187 }
9188
9189 default:
9190 return setError(E_FAIL,
9191 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9192 medium->getLocationFull().c_str(),
9193 mUserData->s.strName.c_str(),
9194 mData->m_strConfigFileFull.c_str());
9195 }
9196
9197 if (FAILED(rc))
9198 break;
9199
9200 /* Bandwidth groups are loaded at this point. */
9201 ComObjPtr<BandwidthGroup> pBwGroup;
9202
9203 if (!dev.strBwGroup.isEmpty())
9204 {
9205 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9206 if (FAILED(rc))
9207 return setError(E_FAIL,
9208 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9209 medium->getLocationFull().c_str(),
9210 dev.strBwGroup.c_str(),
9211 mUserData->s.strName.c_str(),
9212 mData->m_strConfigFileFull.c_str());
9213 pBwGroup->reference();
9214 }
9215
9216 const Bstr controllerName = aStorageController->getName();
9217 ComObjPtr<MediumAttachment> pAttachment;
9218 pAttachment.createObject();
9219 rc = pAttachment->init(this,
9220 medium,
9221 controllerName,
9222 dev.lPort,
9223 dev.lDevice,
9224 dev.deviceType,
9225 false,
9226 dev.fPassThrough,
9227 dev.fTempEject,
9228 dev.fNonRotational,
9229 dev.fDiscard,
9230 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
9231 if (FAILED(rc)) break;
9232
9233 /* associate the medium with this machine and snapshot */
9234 if (!medium.isNull())
9235 {
9236 AutoCaller medCaller(medium);
9237 if (FAILED(medCaller.rc())) return medCaller.rc();
9238 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9239
9240 if (isSnapshotMachine())
9241 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot);
9242 else
9243 rc = medium->addBackReference(mData->mUuid);
9244 /* If the medium->addBackReference fails it sets an appropriate
9245 * error message, so no need to do any guesswork here. */
9246
9247 if (puuidRegistry)
9248 // caller wants registry ID to be set on all attached media (OVF import case)
9249 medium->addRegistry(*puuidRegistry, false /* fRecurse */);
9250 }
9251
9252 if (FAILED(rc))
9253 break;
9254
9255 /* back up mMediaData to let registeredInit() properly rollback on failure
9256 * (= limited accessibility) */
9257 setModified(IsModified_Storage);
9258 mMediaData.backup();
9259 mMediaData->mAttachments.push_back(pAttachment);
9260 }
9261
9262 return rc;
9263}
9264
9265/**
9266 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9267 *
9268 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9269 * @param aSnapshot where to return the found snapshot
9270 * @param aSetError true to set extended error info on failure
9271 */
9272HRESULT Machine::findSnapshotById(const Guid &aId,
9273 ComObjPtr<Snapshot> &aSnapshot,
9274 bool aSetError /* = false */)
9275{
9276 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9277
9278 if (!mData->mFirstSnapshot)
9279 {
9280 if (aSetError)
9281 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9282 return E_FAIL;
9283 }
9284
9285 if (aId.isZero())
9286 aSnapshot = mData->mFirstSnapshot;
9287 else
9288 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref());
9289
9290 if (!aSnapshot)
9291 {
9292 if (aSetError)
9293 return setError(E_FAIL,
9294 tr("Could not find a snapshot with UUID {%s}"),
9295 aId.toString().c_str());
9296 return E_FAIL;
9297 }
9298
9299 return S_OK;
9300}
9301
9302/**
9303 * Returns the snapshot with the given name or fails of no such snapshot.
9304 *
9305 * @param aName snapshot name to find
9306 * @param aSnapshot where to return the found snapshot
9307 * @param aSetError true to set extended error info on failure
9308 */
9309HRESULT Machine::findSnapshotByName(const Utf8Str &strName,
9310 ComObjPtr<Snapshot> &aSnapshot,
9311 bool aSetError /* = false */)
9312{
9313 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9314
9315 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9316
9317 if (!mData->mFirstSnapshot)
9318 {
9319 if (aSetError)
9320 return setError(VBOX_E_OBJECT_NOT_FOUND,
9321 tr("This machine does not have any snapshots"));
9322 return VBOX_E_OBJECT_NOT_FOUND;
9323 }
9324
9325 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName);
9326
9327 if (!aSnapshot)
9328 {
9329 if (aSetError)
9330 return setError(VBOX_E_OBJECT_NOT_FOUND,
9331 tr("Could not find a snapshot named '%s'"), strName.c_str());
9332 return VBOX_E_OBJECT_NOT_FOUND;
9333 }
9334
9335 return S_OK;
9336}
9337
9338/**
9339 * Returns a storage controller object with the given name.
9340 *
9341 * @param aName storage controller name to find
9342 * @param aStorageController where to return the found storage controller
9343 * @param aSetError true to set extended error info on failure
9344 */
9345HRESULT Machine::getStorageControllerByName(const Utf8Str &aName,
9346 ComObjPtr<StorageController> &aStorageController,
9347 bool aSetError /* = false */)
9348{
9349 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9350
9351 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9352 it != mStorageControllers->end();
9353 ++it)
9354 {
9355 if ((*it)->getName() == aName)
9356 {
9357 aStorageController = (*it);
9358 return S_OK;
9359 }
9360 }
9361
9362 if (aSetError)
9363 return setError(VBOX_E_OBJECT_NOT_FOUND,
9364 tr("Could not find a storage controller named '%s'"),
9365 aName.c_str());
9366 return VBOX_E_OBJECT_NOT_FOUND;
9367}
9368
9369HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName,
9370 MediaData::AttachmentList &atts)
9371{
9372 AutoCaller autoCaller(this);
9373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9374
9375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9376
9377 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9378 it != mMediaData->mAttachments.end();
9379 ++it)
9380 {
9381 const ComObjPtr<MediumAttachment> &pAtt = *it;
9382
9383 // should never happen, but deal with NULL pointers in the list.
9384 AssertStmt(!pAtt.isNull(), continue);
9385
9386 // getControllerName() needs caller+read lock
9387 AutoCaller autoAttCaller(pAtt);
9388 if (FAILED(autoAttCaller.rc()))
9389 {
9390 atts.clear();
9391 return autoAttCaller.rc();
9392 }
9393 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9394
9395 if (pAtt->getControllerName() == aName)
9396 atts.push_back(pAtt);
9397 }
9398
9399 return S_OK;
9400}
9401
9402/**
9403 * Helper for #saveSettings. Cares about renaming the settings directory and
9404 * file if the machine name was changed and about creating a new settings file
9405 * if this is a new machine.
9406 *
9407 * @note Must be never called directly but only from #saveSettings().
9408 */
9409HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9410{
9411 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9412
9413 HRESULT rc = S_OK;
9414
9415 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9416
9417 /// @todo need to handle primary group change, too
9418
9419 /* attempt to rename the settings file if machine name is changed */
9420 if ( mUserData->s.fNameSync
9421 && mUserData.isBackedUp()
9422 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9423 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9424 )
9425 {
9426 bool dirRenamed = false;
9427 bool fileRenamed = false;
9428
9429 Utf8Str configFile, newConfigFile;
9430 Utf8Str configFilePrev, newConfigFilePrev;
9431 Utf8Str configDir, newConfigDir;
9432
9433 do
9434 {
9435 int vrc = VINF_SUCCESS;
9436
9437 Utf8Str name = mUserData.backedUpData()->s.strName;
9438 Utf8Str newName = mUserData->s.strName;
9439 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9440 if (group == "/")
9441 group.setNull();
9442 Utf8Str newGroup = mUserData->s.llGroups.front();
9443 if (newGroup == "/")
9444 newGroup.setNull();
9445
9446 configFile = mData->m_strConfigFileFull;
9447
9448 /* first, rename the directory if it matches the group and machine name */
9449 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9450 group.c_str(), RTPATH_DELIMITER, name.c_str());
9451 /** @todo hack, make somehow use of ComposeMachineFilename */
9452 if (mUserData->s.fDirectoryIncludesUUID)
9453 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9454 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9455 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9456 /** @todo hack, make somehow use of ComposeMachineFilename */
9457 if (mUserData->s.fDirectoryIncludesUUID)
9458 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9459 configDir = configFile;
9460 configDir.stripFilename();
9461 newConfigDir = configDir;
9462 if ( configDir.length() >= groupPlusName.length()
9463 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str()))
9464 {
9465 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9466 Utf8Str newConfigBaseDir(newConfigDir);
9467 newConfigDir.append(newGroupPlusName);
9468 /* consistency: use \ if appropriate on the platform */
9469 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9470 /* new dir and old dir cannot be equal here because of 'if'
9471 * above and because name != newName */
9472 Assert(configDir != newConfigDir);
9473 if (!fSettingsFileIsNew)
9474 {
9475 /* perform real rename only if the machine is not new */
9476 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9477 if ( vrc == VERR_FILE_NOT_FOUND
9478 || vrc == VERR_PATH_NOT_FOUND)
9479 {
9480 /* create the parent directory, then retry renaming */
9481 Utf8Str parent(newConfigDir);
9482 parent.stripFilename();
9483 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9484 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9485 }
9486 if (RT_FAILURE(vrc))
9487 {
9488 rc = setError(E_FAIL,
9489 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9490 configDir.c_str(),
9491 newConfigDir.c_str(),
9492 vrc);
9493 break;
9494 }
9495 /* delete subdirectories which are no longer needed */
9496 Utf8Str dir(configDir);
9497 dir.stripFilename();
9498 while (dir != newConfigBaseDir && dir != ".")
9499 {
9500 vrc = RTDirRemove(dir.c_str());
9501 if (RT_FAILURE(vrc))
9502 break;
9503 dir.stripFilename();
9504 }
9505 dirRenamed = true;
9506 }
9507 }
9508
9509 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9510 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9511
9512 /* then try to rename the settings file itself */
9513 if (newConfigFile != configFile)
9514 {
9515 /* get the path to old settings file in renamed directory */
9516 configFile = Utf8StrFmt("%s%c%s",
9517 newConfigDir.c_str(),
9518 RTPATH_DELIMITER,
9519 RTPathFilename(configFile.c_str()));
9520 if (!fSettingsFileIsNew)
9521 {
9522 /* perform real rename only if the machine is not new */
9523 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9524 if (RT_FAILURE(vrc))
9525 {
9526 rc = setError(E_FAIL,
9527 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9528 configFile.c_str(),
9529 newConfigFile.c_str(),
9530 vrc);
9531 break;
9532 }
9533 fileRenamed = true;
9534 configFilePrev = configFile;
9535 configFilePrev += "-prev";
9536 newConfigFilePrev = newConfigFile;
9537 newConfigFilePrev += "-prev";
9538 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9539 }
9540 }
9541
9542 // update m_strConfigFileFull amd mConfigFile
9543 mData->m_strConfigFileFull = newConfigFile;
9544 // compute the relative path too
9545 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9546
9547 // store the old and new so that VirtualBox::saveSettings() can update
9548 // the media registry
9549 if ( mData->mRegistered
9550 && configDir != newConfigDir)
9551 {
9552 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir);
9553
9554 if (pfNeedsGlobalSaveSettings)
9555 *pfNeedsGlobalSaveSettings = true;
9556 }
9557
9558 // in the saved state file path, replace the old directory with the new directory
9559 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9560 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9561
9562 // and do the same thing for the saved state file paths of all the online snapshots
9563 if (mData->mFirstSnapshot)
9564 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
9565 newConfigDir.c_str());
9566 }
9567 while (0);
9568
9569 if (FAILED(rc))
9570 {
9571 /* silently try to rename everything back */
9572 if (fileRenamed)
9573 {
9574 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9575 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9576 }
9577 if (dirRenamed)
9578 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9579 }
9580
9581 if (FAILED(rc)) return rc;
9582 }
9583
9584 if (fSettingsFileIsNew)
9585 {
9586 /* create a virgin config file */
9587 int vrc = VINF_SUCCESS;
9588
9589 /* ensure the settings directory exists */
9590 Utf8Str path(mData->m_strConfigFileFull);
9591 path.stripFilename();
9592 if (!RTDirExists(path.c_str()))
9593 {
9594 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9595 if (RT_FAILURE(vrc))
9596 {
9597 return setError(E_FAIL,
9598 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9599 path.c_str(),
9600 vrc);
9601 }
9602 }
9603
9604 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9605 path = Utf8Str(mData->m_strConfigFileFull);
9606 RTFILE f = NIL_RTFILE;
9607 vrc = RTFileOpen(&f, path.c_str(),
9608 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9609 if (RT_FAILURE(vrc))
9610 return setError(E_FAIL,
9611 tr("Could not create the settings file '%s' (%Rrc)"),
9612 path.c_str(),
9613 vrc);
9614 RTFileClose(f);
9615 }
9616
9617 return rc;
9618}
9619
9620/**
9621 * Saves and commits machine data, user data and hardware data.
9622 *
9623 * Note that on failure, the data remains uncommitted.
9624 *
9625 * @a aFlags may combine the following flags:
9626 *
9627 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9628 * Used when saving settings after an operation that makes them 100%
9629 * correspond to the settings from the current snapshot.
9630 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9631 * #isReallyModified() returns false. This is necessary for cases when we
9632 * change machine data directly, not through the backup()/commit() mechanism.
9633 * - SaveS_Force: settings will be saved without doing a deep compare of the
9634 * settings structures. This is used when this is called because snapshots
9635 * have changed to avoid the overhead of the deep compare.
9636 *
9637 * @note Must be called from under this object's write lock. Locks children for
9638 * writing.
9639 *
9640 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9641 * initialized to false and that will be set to true by this function if
9642 * the caller must invoke VirtualBox::saveSettings() because the global
9643 * settings have changed. This will happen if a machine rename has been
9644 * saved and the global machine and media registries will therefore need
9645 * updating.
9646 */
9647HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings,
9648 int aFlags /*= 0*/)
9649{
9650 LogFlowThisFuncEnter();
9651
9652 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9653
9654 /* make sure child objects are unable to modify the settings while we are
9655 * saving them */
9656 ensureNoStateDependencies();
9657
9658 AssertReturn(!isSnapshotMachine(),
9659 E_FAIL);
9660
9661 HRESULT rc = S_OK;
9662 bool fNeedsWrite = false;
9663
9664 /* First, prepare to save settings. It will care about renaming the
9665 * settings directory and file if the machine name was changed and about
9666 * creating a new settings file if this is a new machine. */
9667 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings);
9668 if (FAILED(rc)) return rc;
9669
9670 // keep a pointer to the current settings structures
9671 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9672 settings::MachineConfigFile *pNewConfig = NULL;
9673
9674 try
9675 {
9676 // make a fresh one to have everyone write stuff into
9677 pNewConfig = new settings::MachineConfigFile(NULL);
9678 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9679
9680 // now go and copy all the settings data from COM to the settings structures
9681 // (this calles saveSettings() on all the COM objects in the machine)
9682 copyMachineDataToSettings(*pNewConfig);
9683
9684 if (aFlags & SaveS_ResetCurStateModified)
9685 {
9686 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9687 mData->mCurrentStateModified = FALSE;
9688 fNeedsWrite = true; // always, no need to compare
9689 }
9690 else if (aFlags & SaveS_Force)
9691 {
9692 fNeedsWrite = true; // always, no need to compare
9693 }
9694 else
9695 {
9696 if (!mData->mCurrentStateModified)
9697 {
9698 // do a deep compare of the settings that we just saved with the settings
9699 // previously stored in the config file; this invokes MachineConfigFile::operator==
9700 // which does a deep compare of all the settings, which is expensive but less expensive
9701 // than writing out XML in vain
9702 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9703
9704 // could still be modified if any settings changed
9705 mData->mCurrentStateModified = fAnySettingsChanged;
9706
9707 fNeedsWrite = fAnySettingsChanged;
9708 }
9709 else
9710 fNeedsWrite = true;
9711 }
9712
9713 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9714
9715 if (fNeedsWrite)
9716 // now spit it all out!
9717 pNewConfig->write(mData->m_strConfigFileFull);
9718
9719 mData->pMachineConfigFile = pNewConfig;
9720 delete pOldConfig;
9721 commit();
9722
9723 // after saving settings, we are no longer different from the XML on disk
9724 mData->flModifications = 0;
9725 }
9726 catch (HRESULT err)
9727 {
9728 // we assume that error info is set by the thrower
9729 rc = err;
9730
9731 // restore old config
9732 delete pNewConfig;
9733 mData->pMachineConfigFile = pOldConfig;
9734 }
9735 catch (...)
9736 {
9737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9738 }
9739
9740 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9741 {
9742 /* Fire the data change event, even on failure (since we've already
9743 * committed all data). This is done only for SessionMachines because
9744 * mutable Machine instances are always not registered (i.e. private
9745 * to the client process that creates them) and thus don't need to
9746 * inform callbacks. */
9747 if (isSessionMachine())
9748 mParent->onMachineDataChange(mData->mUuid);
9749 }
9750
9751 LogFlowThisFunc(("rc=%08X\n", rc));
9752 LogFlowThisFuncLeave();
9753 return rc;
9754}
9755
9756/**
9757 * Implementation for saving the machine settings into the given
9758 * settings::MachineConfigFile instance. This copies machine extradata
9759 * from the previous machine config file in the instance data, if any.
9760 *
9761 * This gets called from two locations:
9762 *
9763 * -- Machine::saveSettings(), during the regular XML writing;
9764 *
9765 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9766 * exported to OVF and we write the VirtualBox proprietary XML
9767 * into a <vbox:Machine> tag.
9768 *
9769 * This routine fills all the fields in there, including snapshots, *except*
9770 * for the following:
9771 *
9772 * -- fCurrentStateModified. There is some special logic associated with that.
9773 *
9774 * The caller can then call MachineConfigFile::write() or do something else
9775 * with it.
9776 *
9777 * Caller must hold the machine lock!
9778 *
9779 * This throws XML errors and HRESULT, so the caller must have a catch block!
9780 */
9781void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
9782{
9783 // deep copy extradata
9784 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9785
9786 config.uuid = mData->mUuid;
9787
9788 // copy name, description, OS type, teleport, UTC etc.
9789 config.machineUserData = mUserData->s;
9790
9791 if ( mData->mMachineState == MachineState_Saved
9792 || mData->mMachineState == MachineState_Restoring
9793 // when deleting a snapshot we may or may not have a saved state in the current state,
9794 // so let's not assert here please
9795 || ( ( mData->mMachineState == MachineState_DeletingSnapshot
9796 || mData->mMachineState == MachineState_DeletingSnapshotOnline
9797 || mData->mMachineState == MachineState_DeletingSnapshotPaused)
9798 && (!mSSData->strStateFilePath.isEmpty())
9799 )
9800 )
9801 {
9802 Assert(!mSSData->strStateFilePath.isEmpty());
9803 /* try to make the file name relative to the settings file dir */
9804 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9805 }
9806 else
9807 {
9808 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9809 config.strStateFile.setNull();
9810 }
9811
9812 if (mData->mCurrentSnapshot)
9813 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId();
9814 else
9815 config.uuidCurrentSnapshot.clear();
9816
9817 config.timeLastStateChange = mData->mLastStateChange;
9818 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9819 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9820
9821 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9822 if (FAILED(rc)) throw rc;
9823
9824 rc = saveStorageControllers(config.storageMachine);
9825 if (FAILED(rc)) throw rc;
9826
9827 // save machine's media registry if this is VirtualBox 4.0 or later
9828 if (config.canHaveOwnMediaRegistry())
9829 {
9830 // determine machine folder
9831 Utf8Str strMachineFolder = getSettingsFileFull();
9832 strMachineFolder.stripFilename();
9833 mParent->saveMediaRegistry(config.mediaRegistry,
9834 getId(), // only media with registry ID == machine UUID
9835 strMachineFolder);
9836 // this throws HRESULT
9837 }
9838
9839 // save snapshots
9840 rc = saveAllSnapshots(config);
9841 if (FAILED(rc)) throw rc;
9842}
9843
9844/**
9845 * Saves all snapshots of the machine into the given machine config file. Called
9846 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9847 * @param config
9848 * @return
9849 */
9850HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config)
9851{
9852 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9853
9854 HRESULT rc = S_OK;
9855
9856 try
9857 {
9858 config.llFirstSnapshot.clear();
9859
9860 if (mData->mFirstSnapshot)
9861 {
9862 settings::Snapshot snapNew;
9863 config.llFirstSnapshot.push_back(snapNew);
9864
9865 // get reference to the fresh copy of the snapshot on the list and
9866 // work on that copy directly to avoid excessive copying later
9867 settings::Snapshot &snap = config.llFirstSnapshot.front();
9868
9869 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/);
9870 if (FAILED(rc)) throw rc;
9871 }
9872
9873// if (mType == IsSessionMachine)
9874// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9875
9876 }
9877 catch (HRESULT err)
9878 {
9879 /* we assume that error info is set by the thrower */
9880 rc = err;
9881 }
9882 catch (...)
9883 {
9884 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9885 }
9886
9887 return rc;
9888}
9889
9890/**
9891 * Saves the VM hardware configuration. It is assumed that the
9892 * given node is empty.
9893 *
9894 * @param data Reference to the settings object for the hardware config.
9895 * @param pDbg Pointer to the settings object for the debugging config
9896 * which happens to live in mHWData.
9897 * @param pAutostart Pointer to the settings object for the autostart config
9898 * which happens to live in mHWData.
9899 */
9900HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9901 settings::Autostart *pAutostart)
9902{
9903 HRESULT rc = S_OK;
9904
9905 try
9906 {
9907 /* The hardware version attribute (optional).
9908 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
9909 if ( mHWData->mHWVersion == "1"
9910 && mSSData->strStateFilePath.isEmpty()
9911 )
9912 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. */
9913
9914 data.strVersion = mHWData->mHWVersion;
9915 data.uuid = mHWData->mHardwareUUID;
9916
9917 // CPU
9918 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
9919 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive;
9920 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
9921 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
9922 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
9923 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
9924 data.fPAE = !!mHWData->mPAEEnabled;
9925 data.enmLongMode = mHWData->mLongMode;
9926 data.fSyntheticCpu = !!mHWData->mSyntheticCpu;
9927
9928 /* Standard and Extended CPUID leafs. */
9929 data.llCpuIdLeafs.clear();
9930 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++)
9931 {
9932 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
9933 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
9934 }
9935 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++)
9936 {
9937 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
9938 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
9939 }
9940
9941 data.cCPUs = mHWData->mCPUCount;
9942 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
9943 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
9944
9945 data.llCpus.clear();
9946 if (data.fCpuHotPlug)
9947 {
9948 for (unsigned idx = 0; idx < data.cCPUs; idx++)
9949 {
9950 if (mHWData->mCPUAttached[idx])
9951 {
9952 settings::Cpu cpu;
9953 cpu.ulId = idx;
9954 data.llCpus.push_back(cpu);
9955 }
9956 }
9957 }
9958
9959 // memory
9960 data.ulMemorySizeMB = mHWData->mMemorySize;
9961 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
9962
9963 // firmware
9964 data.firmwareType = mHWData->mFirmwareType;
9965
9966 // HID
9967 data.pointingHIDType = mHWData->mPointingHIDType;
9968 data.keyboardHIDType = mHWData->mKeyboardHIDType;
9969
9970 // chipset
9971 data.chipsetType = mHWData->mChipsetType;
9972
9973 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled;
9974 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
9975
9976 // HPET
9977 data.fHPETEnabled = !!mHWData->mHPETEnabled;
9978
9979 // boot order
9980 data.mapBootOrder.clear();
9981 for (size_t i = 0;
9982 i < RT_ELEMENTS(mHWData->mBootOrder);
9983 ++i)
9984 data.mapBootOrder[i] = mHWData->mBootOrder[i];
9985
9986 // display
9987 data.graphicsControllerType = mHWData->mGraphicsControllerType;
9988 data.ulVRAMSizeMB = mHWData->mVRAMSize;
9989 data.cMonitors = mHWData->mMonitorCount;
9990 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
9991 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
9992 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
9993 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
9994 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
9995 data.strVideoCaptureFile = mHWData->mVideoCaptureFile;
9996
9997 /* VRDEServer settings (optional) */
9998 rc = mVRDEServer->saveSettings(data.vrdeSettings);
9999 if (FAILED(rc)) throw rc;
10000
10001 /* BIOS (required) */
10002 rc = mBIOSSettings->saveSettings(data.biosSettings);
10003 if (FAILED(rc)) throw rc;
10004
10005 /* USB Controller (required) */
10006 rc = mUSBController->saveSettings(data.usbController);
10007 if (FAILED(rc)) throw rc;
10008
10009 /* Network adapters (required) */
10010 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10011 data.llNetworkAdapters.clear();
10012 /* Write out only the nominal number of network adapters for this
10013 * chipset type. Since Machine::commit() hasn't been called there
10014 * may be extra NIC settings in the vector. */
10015 for (ULONG slot = 0; slot < uMaxNICs; ++slot)
10016 {
10017 settings::NetworkAdapter nic;
10018 nic.ulSlot = slot;
10019 /* paranoia check... must not be NULL, but must not crash either. */
10020 if (mNetworkAdapters[slot])
10021 {
10022 rc = mNetworkAdapters[slot]->saveSettings(nic);
10023 if (FAILED(rc)) throw rc;
10024
10025 data.llNetworkAdapters.push_back(nic);
10026 }
10027 }
10028
10029 /* Serial ports */
10030 data.llSerialPorts.clear();
10031 for (ULONG slot = 0;
10032 slot < RT_ELEMENTS(mSerialPorts);
10033 ++slot)
10034 {
10035 settings::SerialPort s;
10036 s.ulSlot = slot;
10037 rc = mSerialPorts[slot]->saveSettings(s);
10038 if (FAILED(rc)) return rc;
10039
10040 data.llSerialPorts.push_back(s);
10041 }
10042
10043 /* Parallel ports */
10044 data.llParallelPorts.clear();
10045 for (ULONG slot = 0;
10046 slot < RT_ELEMENTS(mParallelPorts);
10047 ++slot)
10048 {
10049 settings::ParallelPort p;
10050 p.ulSlot = slot;
10051 rc = mParallelPorts[slot]->saveSettings(p);
10052 if (FAILED(rc)) return rc;
10053
10054 data.llParallelPorts.push_back(p);
10055 }
10056
10057 /* Audio adapter */
10058 rc = mAudioAdapter->saveSettings(data.audioAdapter);
10059 if (FAILED(rc)) return rc;
10060
10061 /* Shared folders */
10062 data.llSharedFolders.clear();
10063 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10064 it != mHWData->mSharedFolders.end();
10065 ++it)
10066 {
10067 SharedFolder *pSF = *it;
10068 AutoCaller sfCaller(pSF);
10069 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10070 settings::SharedFolder sf;
10071 sf.strName = pSF->getName();
10072 sf.strHostPath = pSF->getHostPath();
10073 sf.fWritable = !!pSF->isWritable();
10074 sf.fAutoMount = !!pSF->isAutoMounted();
10075
10076 data.llSharedFolders.push_back(sf);
10077 }
10078
10079 // clipboard
10080 data.clipboardMode = mHWData->mClipboardMode;
10081
10082 // drag'n'drop
10083 data.dragAndDropMode = mHWData->mDragAndDropMode;
10084
10085 /* Guest */
10086 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10087
10088 // IO settings
10089 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10090 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10091
10092 /* BandwidthControl (required) */
10093 rc = mBandwidthControl->saveSettings(data.ioSettings);
10094 if (FAILED(rc)) throw rc;
10095
10096 /* Host PCI devices */
10097 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10098 it != mHWData->mPCIDeviceAssignments.end();
10099 ++it)
10100 {
10101 ComObjPtr<PCIDeviceAttachment> pda = *it;
10102 settings::HostPCIDeviceAttachment hpda;
10103
10104 rc = pda->saveSettings(hpda);
10105 if (FAILED(rc)) throw rc;
10106
10107 data.pciAttachments.push_back(hpda);
10108 }
10109
10110
10111 // guest properties
10112 data.llGuestProperties.clear();
10113#ifdef VBOX_WITH_GUEST_PROPS
10114 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10115 it != mHWData->mGuestProperties.end();
10116 ++it)
10117 {
10118 HWData::GuestProperty property = it->second;
10119
10120 /* Remove transient guest properties at shutdown unless we
10121 * are saving state */
10122 if ( ( mData->mMachineState == MachineState_PoweredOff
10123 || mData->mMachineState == MachineState_Aborted
10124 || mData->mMachineState == MachineState_Teleported)
10125 && ( property.mFlags & guestProp::TRANSIENT
10126 || property.mFlags & guestProp::TRANSRESET))
10127 continue;
10128 settings::GuestProperty prop;
10129 prop.strName = it->first;
10130 prop.strValue = property.strValue;
10131 prop.timestamp = property.mTimestamp;
10132 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10133 guestProp::writeFlags(property.mFlags, szFlags);
10134 prop.strFlags = szFlags;
10135
10136 data.llGuestProperties.push_back(prop);
10137 }
10138
10139 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns;
10140 /* I presume this doesn't require a backup(). */
10141 mData->mGuestPropertiesModified = FALSE;
10142#endif /* VBOX_WITH_GUEST_PROPS defined */
10143
10144 *pDbg = mHWData->mDebugging;
10145 *pAutostart = mHWData->mAutostart;
10146
10147 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10148 }
10149 catch(std::bad_alloc &)
10150 {
10151 return E_OUTOFMEMORY;
10152 }
10153
10154 AssertComRC(rc);
10155 return rc;
10156}
10157
10158/**
10159 * Saves the storage controller configuration.
10160 *
10161 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10162 */
10163HRESULT Machine::saveStorageControllers(settings::Storage &data)
10164{
10165 data.llStorageControllers.clear();
10166
10167 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10168 it != mStorageControllers->end();
10169 ++it)
10170 {
10171 HRESULT rc;
10172 ComObjPtr<StorageController> pCtl = *it;
10173
10174 settings::StorageController ctl;
10175 ctl.strName = pCtl->getName();
10176 ctl.controllerType = pCtl->getControllerType();
10177 ctl.storageBus = pCtl->getStorageBus();
10178 ctl.ulInstance = pCtl->getInstance();
10179 ctl.fBootable = pCtl->getBootable();
10180
10181 /* Save the port count. */
10182 ULONG portCount;
10183 rc = pCtl->COMGETTER(PortCount)(&portCount);
10184 ComAssertComRCRet(rc, rc);
10185 ctl.ulPortCount = portCount;
10186
10187 /* Save fUseHostIOCache */
10188 BOOL fUseHostIOCache;
10189 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10190 ComAssertComRCRet(rc, rc);
10191 ctl.fUseHostIOCache = !!fUseHostIOCache;
10192
10193 /* Save IDE emulation settings. */
10194 if (ctl.controllerType == StorageControllerType_IntelAhci)
10195 {
10196 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10197 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10198 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10199 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10200 )
10201 ComAssertComRCRet(rc, rc);
10202 }
10203
10204 /* save the devices now. */
10205 rc = saveStorageDevices(pCtl, ctl);
10206 ComAssertComRCRet(rc, rc);
10207
10208 data.llStorageControllers.push_back(ctl);
10209 }
10210
10211 return S_OK;
10212}
10213
10214/**
10215 * Saves the hard disk configuration.
10216 */
10217HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10218 settings::StorageController &data)
10219{
10220 MediaData::AttachmentList atts;
10221
10222 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts);
10223 if (FAILED(rc)) return rc;
10224
10225 data.llAttachedDevices.clear();
10226 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10227 it != atts.end();
10228 ++it)
10229 {
10230 settings::AttachedDevice dev;
10231
10232 MediumAttachment *pAttach = *it;
10233 Medium *pMedium = pAttach->getMedium();
10234
10235 dev.deviceType = pAttach->getType();
10236 dev.lPort = pAttach->getPort();
10237 dev.lDevice = pAttach->getDevice();
10238 if (pMedium)
10239 {
10240 if (pMedium->isHostDrive())
10241 dev.strHostDriveSrc = pMedium->getLocationFull();
10242 else
10243 dev.uuid = pMedium->getId();
10244 dev.fPassThrough = pAttach->getPassthrough();
10245 dev.fTempEject = pAttach->getTempEject();
10246 dev.fNonRotational = pAttach->getNonRotational();
10247 dev.fDiscard = pAttach->getDiscard();
10248 }
10249
10250 dev.strBwGroup = pAttach->getBandwidthGroup();
10251
10252 data.llAttachedDevices.push_back(dev);
10253 }
10254
10255 return S_OK;
10256}
10257
10258/**
10259 * Saves machine state settings as defined by aFlags
10260 * (SaveSTS_* values).
10261 *
10262 * @param aFlags Combination of SaveSTS_* flags.
10263 *
10264 * @note Locks objects for writing.
10265 */
10266HRESULT Machine::saveStateSettings(int aFlags)
10267{
10268 if (aFlags == 0)
10269 return S_OK;
10270
10271 AutoCaller autoCaller(this);
10272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10273
10274 /* This object's write lock is also necessary to serialize file access
10275 * (prevent concurrent reads and writes) */
10276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10277
10278 HRESULT rc = S_OK;
10279
10280 Assert(mData->pMachineConfigFile);
10281
10282 try
10283 {
10284 if (aFlags & SaveSTS_CurStateModified)
10285 mData->pMachineConfigFile->fCurrentStateModified = true;
10286
10287 if (aFlags & SaveSTS_StateFilePath)
10288 {
10289 if (!mSSData->strStateFilePath.isEmpty())
10290 /* try to make the file name relative to the settings file dir */
10291 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10292 else
10293 mData->pMachineConfigFile->strStateFile.setNull();
10294 }
10295
10296 if (aFlags & SaveSTS_StateTimeStamp)
10297 {
10298 Assert( mData->mMachineState != MachineState_Aborted
10299 || mSSData->strStateFilePath.isEmpty());
10300
10301 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10302
10303 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10304//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10305 }
10306
10307 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10308 }
10309 catch (...)
10310 {
10311 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10312 }
10313
10314 return rc;
10315}
10316
10317/**
10318 * Ensures that the given medium is added to a media registry. If this machine
10319 * was created with 4.0 or later, then the machine registry is used. Otherwise
10320 * the global VirtualBox media registry is used.
10321 *
10322 * Caller must NOT hold machine lock, media tree or any medium locks!
10323 *
10324 * @param pMedium
10325 */
10326void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10327{
10328 /* Paranoia checks: do not hold machine or media tree locks. */
10329 AssertReturnVoid(!isWriteLockOnCurrentThread());
10330 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10331
10332 ComObjPtr<Medium> pBase;
10333 {
10334 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10335 pBase = pMedium->getBase();
10336 }
10337
10338 /* Paranoia checks: do not hold medium locks. */
10339 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10340 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10341
10342 // decide which medium registry to use now that the medium is attached:
10343 Guid uuid;
10344 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10345 // machine XML is VirtualBox 4.0 or higher:
10346 uuid = getId(); // machine UUID
10347 else
10348 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID
10349
10350 if (pMedium->addRegistry(uuid, false /* fRecurse */))
10351 mParent->markRegistryModified(uuid);
10352
10353 /* For more complex hard disk structures it can happen that the base
10354 * medium isn't yet associated with any medium registry. Do that now. */
10355 if (pMedium != pBase)
10356 {
10357 if (pBase->addRegistry(uuid, true /* fRecurse */))
10358 mParent->markRegistryModified(uuid);
10359 }
10360}
10361
10362/**
10363 * Creates differencing hard disks for all normal hard disks attached to this
10364 * machine and a new set of attachments to refer to created disks.
10365 *
10366 * Used when taking a snapshot or when deleting the current state. Gets called
10367 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10368 *
10369 * This method assumes that mMediaData contains the original hard disk attachments
10370 * it needs to create diffs for. On success, these attachments will be replaced
10371 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10372 * called to delete created diffs which will also rollback mMediaData and restore
10373 * whatever was backed up before calling this method.
10374 *
10375 * Attachments with non-normal hard disks are left as is.
10376 *
10377 * If @a aOnline is @c false then the original hard disks that require implicit
10378 * diffs will be locked for reading. Otherwise it is assumed that they are
10379 * already locked for writing (when the VM was started). Note that in the latter
10380 * case it is responsibility of the caller to lock the newly created diffs for
10381 * writing if this method succeeds.
10382 *
10383 * @param aProgress Progress object to run (must contain at least as
10384 * many operations left as the number of hard disks
10385 * attached).
10386 * @param aOnline Whether the VM was online prior to this operation.
10387 *
10388 * @note The progress object is not marked as completed, neither on success nor
10389 * on failure. This is a responsibility of the caller.
10390 *
10391 * @note Locks this object and the media tree for writing.
10392 */
10393HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
10394 ULONG aWeight,
10395 bool aOnline)
10396{
10397 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10398
10399 AutoCaller autoCaller(this);
10400 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10401
10402 AutoMultiWriteLock2 alock(this->lockHandle(),
10403 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10404
10405 /* must be in a protective state because we release the lock below */
10406 AssertReturn( mData->mMachineState == MachineState_Saving
10407 || mData->mMachineState == MachineState_LiveSnapshotting
10408 || mData->mMachineState == MachineState_RestoringSnapshot
10409 || mData->mMachineState == MachineState_DeletingSnapshot
10410 , E_FAIL);
10411
10412 HRESULT rc = S_OK;
10413
10414 // use appropriate locked media map (online or offline)
10415 MediumLockListMap lockedMediaOffline;
10416 MediumLockListMap *lockedMediaMap;
10417 if (aOnline)
10418 lockedMediaMap = &mData->mSession.mLockedMedia;
10419 else
10420 lockedMediaMap = &lockedMediaOffline;
10421
10422 try
10423 {
10424 if (!aOnline)
10425 {
10426 /* lock all attached hard disks early to detect "in use"
10427 * situations before creating actual diffs */
10428 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10429 it != mMediaData->mAttachments.end();
10430 ++it)
10431 {
10432 MediumAttachment* pAtt = *it;
10433 if (pAtt->getType() == DeviceType_HardDisk)
10434 {
10435 Medium* pMedium = pAtt->getMedium();
10436 Assert(pMedium);
10437
10438 MediumLockList *pMediumLockList(new MediumLockList());
10439 alock.release();
10440 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10441 false /* fMediumLockWrite */,
10442 NULL,
10443 *pMediumLockList);
10444 alock.acquire();
10445 if (FAILED(rc))
10446 {
10447 delete pMediumLockList;
10448 throw rc;
10449 }
10450 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10451 if (FAILED(rc))
10452 {
10453 throw setError(rc,
10454 tr("Collecting locking information for all attached media failed"));
10455 }
10456 }
10457 }
10458
10459 /* Now lock all media. If this fails, nothing is locked. */
10460 alock.release();
10461 rc = lockedMediaMap->Lock();
10462 alock.acquire();
10463 if (FAILED(rc))
10464 {
10465 throw setError(rc,
10466 tr("Locking of attached media failed"));
10467 }
10468 }
10469
10470 /* remember the current list (note that we don't use backup() since
10471 * mMediaData may be already backed up) */
10472 MediaData::AttachmentList atts = mMediaData->mAttachments;
10473
10474 /* start from scratch */
10475 mMediaData->mAttachments.clear();
10476
10477 /* go through remembered attachments and create diffs for normal hard
10478 * disks and attach them */
10479 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10480 it != atts.end();
10481 ++it)
10482 {
10483 MediumAttachment* pAtt = *it;
10484
10485 DeviceType_T devType = pAtt->getType();
10486 Medium* pMedium = pAtt->getMedium();
10487
10488 if ( devType != DeviceType_HardDisk
10489 || pMedium == NULL
10490 || pMedium->getType() != MediumType_Normal)
10491 {
10492 /* copy the attachment as is */
10493
10494 /** @todo the progress object created in Console::TakeSnaphot
10495 * only expects operations for hard disks. Later other
10496 * device types need to show up in the progress as well. */
10497 if (devType == DeviceType_HardDisk)
10498 {
10499 if (pMedium == NULL)
10500 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10501 aWeight); // weight
10502 else
10503 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10504 pMedium->getBase()->getName().c_str()).raw(),
10505 aWeight); // weight
10506 }
10507
10508 mMediaData->mAttachments.push_back(pAtt);
10509 continue;
10510 }
10511
10512 /* need a diff */
10513 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10514 pMedium->getBase()->getName().c_str()).raw(),
10515 aWeight); // weight
10516
10517 Utf8Str strFullSnapshotFolder;
10518 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10519
10520 ComObjPtr<Medium> diff;
10521 diff.createObject();
10522 // store the diff in the same registry as the parent
10523 // (this cannot fail here because we can't create implicit diffs for
10524 // unregistered images)
10525 Guid uuidRegistryParent;
10526 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent);
10527 Assert(fInRegistry); NOREF(fInRegistry);
10528 rc = diff->init(mParent,
10529 pMedium->getPreferredDiffFormat(),
10530 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10531 uuidRegistryParent);
10532 if (FAILED(rc)) throw rc;
10533
10534 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10535 * the push_back? Looks like we're going to release medium with the
10536 * wrong kind of lock (general issue with if we fail anywhere at all)
10537 * and an orphaned VDI in the snapshots folder. */
10538
10539 /* update the appropriate lock list */
10540 MediumLockList *pMediumLockList;
10541 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10542 AssertComRCThrowRC(rc);
10543 if (aOnline)
10544 {
10545 alock.release();
10546 /* The currently attached medium will be read-only, change
10547 * the lock type to read. */
10548 rc = pMediumLockList->Update(pMedium, false);
10549 alock.acquire();
10550 AssertComRCThrowRC(rc);
10551 }
10552
10553 /* release the locks before the potentially lengthy operation */
10554 alock.release();
10555 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,
10556 pMediumLockList,
10557 NULL /* aProgress */,
10558 true /* aWait */);
10559 alock.acquire();
10560 if (FAILED(rc)) throw rc;
10561
10562 rc = lockedMediaMap->Unlock();
10563 AssertComRCThrowRC(rc);
10564 alock.release();
10565 rc = pMediumLockList->Append(diff, true);
10566 alock.acquire();
10567 AssertComRCThrowRC(rc);
10568 alock.release();
10569 rc = lockedMediaMap->Lock();
10570 alock.acquire();
10571 AssertComRCThrowRC(rc);
10572
10573 rc = diff->addBackReference(mData->mUuid);
10574 AssertComRCThrowRC(rc);
10575
10576 /* add a new attachment */
10577 ComObjPtr<MediumAttachment> attachment;
10578 attachment.createObject();
10579 rc = attachment->init(this,
10580 diff,
10581 pAtt->getControllerName(),
10582 pAtt->getPort(),
10583 pAtt->getDevice(),
10584 DeviceType_HardDisk,
10585 true /* aImplicit */,
10586 false /* aPassthrough */,
10587 false /* aTempEject */,
10588 pAtt->getNonRotational(),
10589 pAtt->getDiscard(),
10590 pAtt->getBandwidthGroup());
10591 if (FAILED(rc)) throw rc;
10592
10593 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10594 AssertComRCThrowRC(rc);
10595 mMediaData->mAttachments.push_back(attachment);
10596 }
10597 }
10598 catch (HRESULT aRC) { rc = aRC; }
10599
10600 /* unlock all hard disks we locked when there is no VM */
10601 if (!aOnline)
10602 {
10603 ErrorInfoKeeper eik;
10604
10605 HRESULT rc1 = lockedMediaMap->Clear();
10606 AssertComRC(rc1);
10607 }
10608
10609 return rc;
10610}
10611
10612/**
10613 * Deletes implicit differencing hard disks created either by
10614 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10615 *
10616 * Note that to delete hard disks created by #AttachDevice() this method is
10617 * called from #fixupMedia() when the changes are rolled back.
10618 *
10619 * @note Locks this object and the media tree for writing.
10620 */
10621HRESULT Machine::deleteImplicitDiffs(bool aOnline)
10622{
10623 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10624
10625 AutoCaller autoCaller(this);
10626 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10627
10628 AutoMultiWriteLock2 alock(this->lockHandle(),
10629 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10630
10631 /* We absolutely must have backed up state. */
10632 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10633
10634 /* Check if there are any implicitly created diff images. */
10635 bool fImplicitDiffs = false;
10636 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10637 it != mMediaData->mAttachments.end();
10638 ++it)
10639 {
10640 const ComObjPtr<MediumAttachment> &pAtt = *it;
10641 if (pAtt->isImplicit())
10642 {
10643 fImplicitDiffs = true;
10644 break;
10645 }
10646 }
10647 /* If there is nothing to do, leave early. This saves lots of image locking
10648 * effort. It also avoids a MachineStateChanged event without real reason.
10649 * This is important e.g. when loading a VM config, because there should be
10650 * no events. Otherwise API clients can become thoroughly confused for
10651 * inaccessible VMs (the code for loading VM configs uses this method for
10652 * cleanup if the config makes no sense), as they take such events as an
10653 * indication that the VM is alive, and they would force the VM config to
10654 * be reread, leading to an endless loop. */
10655 if (!fImplicitDiffs)
10656 return S_OK;
10657
10658 HRESULT rc = S_OK;
10659 MachineState_T oldState = mData->mMachineState;
10660
10661 /* will release the lock before the potentially lengthy operation,
10662 * so protect with the special state (unless already protected) */
10663 if ( oldState != MachineState_Saving
10664 && oldState != MachineState_LiveSnapshotting
10665 && oldState != MachineState_RestoringSnapshot
10666 && oldState != MachineState_DeletingSnapshot
10667 && oldState != MachineState_DeletingSnapshotOnline
10668 && oldState != MachineState_DeletingSnapshotPaused
10669 )
10670 setMachineState(MachineState_SettingUp);
10671
10672 // use appropriate locked media map (online or offline)
10673 MediumLockListMap lockedMediaOffline;
10674 MediumLockListMap *lockedMediaMap;
10675 if (aOnline)
10676 lockedMediaMap = &mData->mSession.mLockedMedia;
10677 else
10678 lockedMediaMap = &lockedMediaOffline;
10679
10680 try
10681 {
10682 if (!aOnline)
10683 {
10684 /* lock all attached hard disks early to detect "in use"
10685 * situations before deleting actual diffs */
10686 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10687 it != mMediaData->mAttachments.end();
10688 ++it)
10689 {
10690 MediumAttachment* pAtt = *it;
10691 if (pAtt->getType() == DeviceType_HardDisk)
10692 {
10693 Medium* pMedium = pAtt->getMedium();
10694 Assert(pMedium);
10695
10696 MediumLockList *pMediumLockList(new MediumLockList());
10697 alock.release();
10698 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,
10699 false /* fMediumLockWrite */,
10700 NULL,
10701 *pMediumLockList);
10702 alock.acquire();
10703
10704 if (FAILED(rc))
10705 {
10706 delete pMediumLockList;
10707 throw rc;
10708 }
10709
10710 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10711 if (FAILED(rc))
10712 throw rc;
10713 }
10714 }
10715
10716 if (FAILED(rc))
10717 throw rc;
10718 } // end of offline
10719
10720 /* Lock lists are now up to date and include implicitly created media */
10721
10722 /* Go through remembered attachments and delete all implicitly created
10723 * diffs and fix up the attachment information */
10724 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10725 MediaData::AttachmentList implicitAtts;
10726 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10727 it != mMediaData->mAttachments.end();
10728 ++it)
10729 {
10730 ComObjPtr<MediumAttachment> pAtt = *it;
10731 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10732 if (pMedium.isNull())
10733 continue;
10734
10735 // Implicit attachments go on the list for deletion and back references are removed.
10736 if (pAtt->isImplicit())
10737 {
10738 /* Deassociate and mark for deletion */
10739 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName()));
10740 rc = pMedium->removeBackReference(mData->mUuid);
10741 if (FAILED(rc))
10742 throw rc;
10743 implicitAtts.push_back(pAtt);
10744 continue;
10745 }
10746
10747 /* Was this medium attached before? */
10748 if (!findAttachment(oldAtts, pMedium))
10749 {
10750 /* no: de-associate */
10751 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName()));
10752 rc = pMedium->removeBackReference(mData->mUuid);
10753 if (FAILED(rc))
10754 throw rc;
10755 continue;
10756 }
10757 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName()));
10758 }
10759
10760 /* If there are implicit attachments to delete, throw away the lock
10761 * map contents (which will unlock all media) since the medium
10762 * attachments will be rolled back. Below we need to completely
10763 * recreate the lock map anyway since it is infinitely complex to
10764 * do this incrementally (would need reconstructing each attachment
10765 * change, which would be extremely hairy). */
10766 if (implicitAtts.size() != 0)
10767 {
10768 ErrorInfoKeeper eik;
10769
10770 HRESULT rc1 = lockedMediaMap->Clear();
10771 AssertComRC(rc1);
10772 }
10773
10774 /* rollback hard disk changes */
10775 mMediaData.rollback();
10776
10777 MultiResult mrc(S_OK);
10778
10779 // Delete unused implicit diffs.
10780 if (implicitAtts.size() != 0)
10781 {
10782 alock.release();
10783
10784 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();
10785 it != implicitAtts.end();
10786 ++it)
10787 {
10788 // Remove medium associated with this attachment.
10789 ComObjPtr<MediumAttachment> pAtt = *it;
10790 Assert(pAtt);
10791 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName()));
10792 ComObjPtr<Medium> pMedium = pAtt->getMedium();
10793 Assert(pMedium);
10794
10795 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/);
10796 // continue on delete failure, just collect error messages
10797 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() ));
10798 mrc = rc;
10799 }
10800
10801 alock.acquire();
10802
10803 /* if there is a VM recreate media lock map as mentioned above,
10804 * otherwise it is a waste of time and we leave things unlocked */
10805 if (aOnline)
10806 {
10807 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10808 /* must never be NULL, but better safe than sorry */
10809 if (!pMachine.isNull())
10810 {
10811 alock.release();
10812 rc = mData->mSession.mMachine->lockMedia();
10813 alock.acquire();
10814 if (FAILED(rc))
10815 throw rc;
10816 }
10817 }
10818 }
10819 }
10820 catch (HRESULT aRC) {rc = aRC;}
10821
10822 if (mData->mMachineState == MachineState_SettingUp)
10823 setMachineState(oldState);
10824
10825 /* unlock all hard disks we locked when there is no VM */
10826 if (!aOnline)
10827 {
10828 ErrorInfoKeeper eik;
10829
10830 HRESULT rc1 = lockedMediaMap->Clear();
10831 AssertComRC(rc1);
10832 }
10833
10834 return rc;
10835}
10836
10837
10838/**
10839 * Looks through the given list of media attachments for one with the given parameters
10840 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10841 * can be searched as well if needed.
10842 *
10843 * @param list
10844 * @param aControllerName
10845 * @param aControllerPort
10846 * @param aDevice
10847 * @return
10848 */
10849MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10850 IN_BSTR aControllerName,
10851 LONG aControllerPort,
10852 LONG aDevice)
10853{
10854 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10855 it != ll.end();
10856 ++it)
10857 {
10858 MediumAttachment *pAttach = *it;
10859 if (pAttach->matches(aControllerName, aControllerPort, aDevice))
10860 return pAttach;
10861 }
10862
10863 return NULL;
10864}
10865
10866/**
10867 * Looks through the given list of media attachments for one with the given parameters
10868 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10869 * can be searched as well if needed.
10870 *
10871 * @param list
10872 * @param aControllerName
10873 * @param aControllerPort
10874 * @param aDevice
10875 * @return
10876 */
10877MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10878 ComObjPtr<Medium> pMedium)
10879{
10880 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10881 it != ll.end();
10882 ++it)
10883 {
10884 MediumAttachment *pAttach = *it;
10885 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10886 if (pMediumThis == pMedium)
10887 return pAttach;
10888 }
10889
10890 return NULL;
10891}
10892
10893/**
10894 * Looks through the given list of media attachments for one with the given parameters
10895 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10896 * can be searched as well if needed.
10897 *
10898 * @param list
10899 * @param aControllerName
10900 * @param aControllerPort
10901 * @param aDevice
10902 * @return
10903 */
10904MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,
10905 Guid &id)
10906{
10907 for (MediaData::AttachmentList::const_iterator it = ll.begin();
10908 it != ll.end();
10909 ++it)
10910 {
10911 MediumAttachment *pAttach = *it;
10912 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();
10913 if (pMediumThis->getId() == id)
10914 return pAttach;
10915 }
10916
10917 return NULL;
10918}
10919
10920/**
10921 * Main implementation for Machine::DetachDevice. This also gets called
10922 * from Machine::prepareUnregister() so it has been taken out for simplicity.
10923 *
10924 * @param pAttach Medium attachment to detach.
10925 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
10926 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.
10927 * @return
10928 */
10929HRESULT Machine::detachDevice(MediumAttachment *pAttach,
10930 AutoWriteLock &writeLock,
10931 Snapshot *pSnapshot)
10932{
10933 ComObjPtr<Medium> oldmedium = pAttach->getMedium();
10934 DeviceType_T mediumType = pAttach->getType();
10935
10936 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));
10937
10938 if (pAttach->isImplicit())
10939 {
10940 /* attempt to implicitly delete the implicitly created diff */
10941
10942 /// @todo move the implicit flag from MediumAttachment to Medium
10943 /// and forbid any hard disk operation when it is implicit. Or maybe
10944 /// a special media state for it to make it even more simple.
10945
10946 Assert(mMediaData.isBackedUp());
10947
10948 /* will release the lock before the potentially lengthy operation, so
10949 * protect with the special state */
10950 MachineState_T oldState = mData->mMachineState;
10951 setMachineState(MachineState_SettingUp);
10952
10953 writeLock.release();
10954
10955 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/,
10956 true /*aWait*/);
10957
10958 writeLock.acquire();
10959
10960 setMachineState(oldState);
10961
10962 if (FAILED(rc)) return rc;
10963 }
10964
10965 setModified(IsModified_Storage);
10966 mMediaData.backup();
10967 mMediaData->mAttachments.remove(pAttach);
10968
10969 if (!oldmedium.isNull())
10970 {
10971 // if this is from a snapshot, do not defer detachment to commitMedia()
10972 if (pSnapshot)
10973 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());
10974 // else if non-hard disk media, do not defer detachment to commitMedia() either
10975 else if (mediumType != DeviceType_HardDisk)
10976 oldmedium->removeBackReference(mData->mUuid);
10977 }
10978
10979 return S_OK;
10980}
10981
10982/**
10983 * Goes thru all media of the given list and
10984 *
10985 * 1) calls detachDevice() on each of them for this machine and
10986 * 2) adds all Medium objects found in the process to the given list,
10987 * depending on cleanupMode.
10988 *
10989 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
10990 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
10991 * media to the list.
10992 *
10993 * This gets called from Machine::Unregister, both for the actual Machine and
10994 * the SnapshotMachine objects that might be found in the snapshots.
10995 *
10996 * Requires caller and locking. The machine lock must be passed in because it
10997 * will be passed on to detachDevice which needs it for temporary unlocking.
10998 *
10999 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.
11000 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11001 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;
11002 * otherwise no media get added.
11003 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11004 * @return
11005 */
11006HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
11007 Snapshot *pSnapshot,
11008 CleanupMode_T cleanupMode,
11009 MediaList &llMedia)
11010{
11011 Assert(isWriteLockOnCurrentThread());
11012
11013 HRESULT rc;
11014
11015 // make a temporary list because detachDevice invalidates iterators into
11016 // mMediaData->mAttachments
11017 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11018
11019 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
11020 it != llAttachments2.end();
11021 ++it)
11022 {
11023 ComObjPtr<MediumAttachment> &pAttach = *it;
11024 ComObjPtr<Medium> pMedium = pAttach->getMedium();
11025
11026 if (!pMedium.isNull())
11027 {
11028 AutoCaller mac(pMedium);
11029 if (FAILED(mac.rc())) return mac.rc();
11030 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11031 DeviceType_T devType = pMedium->getDeviceType();
11032 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11033 && devType == DeviceType_HardDisk)
11034 || (cleanupMode == CleanupMode_Full)
11035 )
11036 {
11037 llMedia.push_back(pMedium);
11038 ComObjPtr<Medium> pParent = pMedium->getParent();
11039 /*
11040 * Search for medias which are not attached to any machine, but
11041 * in the chain to an attached disk. Mediums are only consided
11042 * if they are:
11043 * - have only one child
11044 * - no references to any machines
11045 * - are of normal medium type
11046 */
11047 while (!pParent.isNull())
11048 {
11049 AutoCaller mac1(pParent);
11050 if (FAILED(mac1.rc())) return mac1.rc();
11051 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11052 if (pParent->getChildren().size() == 1)
11053 {
11054 if ( pParent->getMachineBackRefCount() == 0
11055 && pParent->getType() == MediumType_Normal
11056 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11057 llMedia.push_back(pParent);
11058 }
11059 else
11060 break;
11061 pParent = pParent->getParent();
11062 }
11063 }
11064 }
11065
11066 // real machine: then we need to use the proper method
11067 rc = detachDevice(pAttach, writeLock, pSnapshot);
11068
11069 if (FAILED(rc))
11070 return rc;
11071 }
11072
11073 return S_OK;
11074}
11075
11076/**
11077 * Perform deferred hard disk detachments.
11078 *
11079 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11080 * backed up).
11081 *
11082 * If @a aOnline is @c true then this method will also unlock the old hard disks
11083 * for which the new implicit diffs were created and will lock these new diffs for
11084 * writing.
11085 *
11086 * @param aOnline Whether the VM was online prior to this operation.
11087 *
11088 * @note Locks this object for writing!
11089 */
11090void Machine::commitMedia(bool aOnline /*= false*/)
11091{
11092 AutoCaller autoCaller(this);
11093 AssertComRCReturnVoid(autoCaller.rc());
11094
11095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11096
11097 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11098
11099 HRESULT rc = S_OK;
11100
11101 /* no attach/detach operations -- nothing to do */
11102 if (!mMediaData.isBackedUp())
11103 return;
11104
11105 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11106 bool fMediaNeedsLocking = false;
11107
11108 /* enumerate new attachments */
11109 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11110 it != mMediaData->mAttachments.end();
11111 ++it)
11112 {
11113 MediumAttachment *pAttach = *it;
11114
11115 pAttach->commit();
11116
11117 Medium* pMedium = pAttach->getMedium();
11118 bool fImplicit = pAttach->isImplicit();
11119
11120 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11121 (pMedium) ? pMedium->getName().c_str() : "NULL",
11122 fImplicit));
11123
11124 /** @todo convert all this Machine-based voodoo to MediumAttachment
11125 * based commit logic. */
11126 if (fImplicit)
11127 {
11128 /* convert implicit attachment to normal */
11129 pAttach->setImplicit(false);
11130
11131 if ( aOnline
11132 && pMedium
11133 && pAttach->getType() == DeviceType_HardDisk
11134 )
11135 {
11136 ComObjPtr<Medium> parent = pMedium->getParent();
11137 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);
11138
11139 /* update the appropriate lock list */
11140 MediumLockList *pMediumLockList;
11141 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11142 AssertComRC(rc);
11143 if (pMediumLockList)
11144 {
11145 /* unlock if there's a need to change the locking */
11146 if (!fMediaNeedsLocking)
11147 {
11148 rc = mData->mSession.mLockedMedia.Unlock();
11149 AssertComRC(rc);
11150 fMediaNeedsLocking = true;
11151 }
11152 rc = pMediumLockList->Update(parent, false);
11153 AssertComRC(rc);
11154 rc = pMediumLockList->Append(pMedium, true);
11155 AssertComRC(rc);
11156 }
11157 }
11158
11159 continue;
11160 }
11161
11162 if (pMedium)
11163 {
11164 /* was this medium attached before? */
11165 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();
11166 oldIt != oldAtts.end();
11167 ++oldIt)
11168 {
11169 MediumAttachment *pOldAttach = *oldIt;
11170 if (pOldAttach->getMedium() == pMedium)
11171 {
11172 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));
11173
11174 /* yes: remove from old to avoid de-association */
11175 oldAtts.erase(oldIt);
11176 break;
11177 }
11178 }
11179 }
11180 }
11181
11182 /* enumerate remaining old attachments and de-associate from the
11183 * current machine state */
11184 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();
11185 it != oldAtts.end();
11186 ++it)
11187 {
11188 MediumAttachment *pAttach = *it;
11189 Medium* pMedium = pAttach->getMedium();
11190
11191 /* Detach only hard disks, since DVD/floppy media is detached
11192 * instantly in MountMedium. */
11193 if (pAttach->getType() == DeviceType_HardDisk && pMedium)
11194 {
11195 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));
11196
11197 /* now de-associate from the current machine state */
11198 rc = pMedium->removeBackReference(mData->mUuid);
11199 AssertComRC(rc);
11200
11201 if (aOnline)
11202 {
11203 /* unlock since medium is not used anymore */
11204 MediumLockList *pMediumLockList;
11205 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11206 AssertComRC(rc);
11207 if (pMediumLockList)
11208 {
11209 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11210 AssertComRC(rc);
11211 }
11212 }
11213 }
11214 }
11215
11216 /* take media locks again so that the locking state is consistent */
11217 if (fMediaNeedsLocking)
11218 {
11219 Assert(aOnline);
11220 rc = mData->mSession.mLockedMedia.Lock();
11221 AssertComRC(rc);
11222 }
11223
11224 /* commit the hard disk changes */
11225 mMediaData.commit();
11226
11227 if (isSessionMachine())
11228 {
11229 /*
11230 * Update the parent machine to point to the new owner.
11231 * This is necessary because the stored parent will point to the
11232 * session machine otherwise and cause crashes or errors later
11233 * when the session machine gets invalid.
11234 */
11235 /** @todo Change the MediumAttachment class to behave like any other
11236 * class in this regard by creating peer MediumAttachment
11237 * objects for session machines and share the data with the peer
11238 * machine.
11239 */
11240 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11241 it != mMediaData->mAttachments.end();
11242 ++it)
11243 {
11244 (*it)->updateParentMachine(mPeer);
11245 }
11246
11247 /* attach new data to the primary machine and reshare it */
11248 mPeer->mMediaData.attach(mMediaData);
11249 }
11250
11251 return;
11252}
11253
11254/**
11255 * Perform deferred deletion of implicitly created diffs.
11256 *
11257 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11258 * backed up).
11259 *
11260 * @note Locks this object for writing!
11261 */
11262void Machine::rollbackMedia()
11263{
11264 AutoCaller autoCaller(this);
11265 AssertComRCReturnVoid(autoCaller.rc());
11266
11267 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11268 LogFlowThisFunc(("Entering rollbackMedia\n"));
11269
11270 HRESULT rc = S_OK;
11271
11272 /* no attach/detach operations -- nothing to do */
11273 if (!mMediaData.isBackedUp())
11274 return;
11275
11276 /* enumerate new attachments */
11277 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11278 it != mMediaData->mAttachments.end();
11279 ++it)
11280 {
11281 MediumAttachment *pAttach = *it;
11282 /* Fix up the backrefs for DVD/floppy media. */
11283 if (pAttach->getType() != DeviceType_HardDisk)
11284 {
11285 Medium* pMedium = pAttach->getMedium();
11286 if (pMedium)
11287 {
11288 rc = pMedium->removeBackReference(mData->mUuid);
11289 AssertComRC(rc);
11290 }
11291 }
11292
11293 (*it)->rollback();
11294
11295 pAttach = *it;
11296 /* Fix up the backrefs for DVD/floppy media. */
11297 if (pAttach->getType() != DeviceType_HardDisk)
11298 {
11299 Medium* pMedium = pAttach->getMedium();
11300 if (pMedium)
11301 {
11302 rc = pMedium->addBackReference(mData->mUuid);
11303 AssertComRC(rc);
11304 }
11305 }
11306 }
11307
11308 /** @todo convert all this Machine-based voodoo to MediumAttachment
11309 * based rollback logic. */
11310 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11311
11312 return;
11313}
11314
11315/**
11316 * Returns true if the settings file is located in the directory named exactly
11317 * as the machine; this means, among other things, that the machine directory
11318 * should be auto-renamed.
11319 *
11320 * @param aSettingsDir if not NULL, the full machine settings file directory
11321 * name will be assigned there.
11322 *
11323 * @note Doesn't lock anything.
11324 * @note Not thread safe (must be called from this object's lock).
11325 */
11326bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11327{
11328 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11329 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11330 if (aSettingsDir)
11331 *aSettingsDir = strMachineDirName;
11332 strMachineDirName.stripPath(); // vmname
11333 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11334 strConfigFileOnly.stripPath() // vmname.vbox
11335 .stripExt(); // vmname
11336 /** @todo hack, make somehow use of ComposeMachineFilename */
11337 if (mUserData->s.fDirectoryIncludesUUID)
11338 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11339
11340 AssertReturn(!strMachineDirName.isEmpty(), false);
11341 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11342
11343 return strMachineDirName == strConfigFileOnly;
11344}
11345
11346/**
11347 * Discards all changes to machine settings.
11348 *
11349 * @param aNotify Whether to notify the direct session about changes or not.
11350 *
11351 * @note Locks objects for writing!
11352 */
11353void Machine::rollback(bool aNotify)
11354{
11355 AutoCaller autoCaller(this);
11356 AssertComRCReturn(autoCaller.rc(), (void)0);
11357
11358 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11359
11360 if (!mStorageControllers.isNull())
11361 {
11362 if (mStorageControllers.isBackedUp())
11363 {
11364 /* unitialize all new devices (absent in the backed up list). */
11365 StorageControllerList::const_iterator it = mStorageControllers->begin();
11366 StorageControllerList *backedList = mStorageControllers.backedUpData();
11367 while (it != mStorageControllers->end())
11368 {
11369 if ( std::find(backedList->begin(), backedList->end(), *it)
11370 == backedList->end()
11371 )
11372 {
11373 (*it)->uninit();
11374 }
11375 ++it;
11376 }
11377
11378 /* restore the list */
11379 mStorageControllers.rollback();
11380 }
11381
11382 /* rollback any changes to devices after restoring the list */
11383 if (mData->flModifications & IsModified_Storage)
11384 {
11385 StorageControllerList::const_iterator it = mStorageControllers->begin();
11386 while (it != mStorageControllers->end())
11387 {
11388 (*it)->rollback();
11389 ++it;
11390 }
11391 }
11392 }
11393
11394 mUserData.rollback();
11395
11396 mHWData.rollback();
11397
11398 if (mData->flModifications & IsModified_Storage)
11399 rollbackMedia();
11400
11401 if (mBIOSSettings)
11402 mBIOSSettings->rollback();
11403
11404 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11405 mVRDEServer->rollback();
11406
11407 if (mAudioAdapter)
11408 mAudioAdapter->rollback();
11409
11410 if (mUSBController && (mData->flModifications & IsModified_USB))
11411 mUSBController->rollback();
11412
11413 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11414 mBandwidthControl->rollback();
11415
11416 if (!mHWData.isNull())
11417 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11418 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11419 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11420 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11421
11422 if (mData->flModifications & IsModified_NetworkAdapters)
11423 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11424 if ( mNetworkAdapters[slot]
11425 && mNetworkAdapters[slot]->isModified())
11426 {
11427 mNetworkAdapters[slot]->rollback();
11428 networkAdapters[slot] = mNetworkAdapters[slot];
11429 }
11430
11431 if (mData->flModifications & IsModified_SerialPorts)
11432 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11433 if ( mSerialPorts[slot]
11434 && mSerialPorts[slot]->isModified())
11435 {
11436 mSerialPorts[slot]->rollback();
11437 serialPorts[slot] = mSerialPorts[slot];
11438 }
11439
11440 if (mData->flModifications & IsModified_ParallelPorts)
11441 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11442 if ( mParallelPorts[slot]
11443 && mParallelPorts[slot]->isModified())
11444 {
11445 mParallelPorts[slot]->rollback();
11446 parallelPorts[slot] = mParallelPorts[slot];
11447 }
11448
11449 if (aNotify)
11450 {
11451 /* inform the direct session about changes */
11452
11453 ComObjPtr<Machine> that = this;
11454 uint32_t flModifications = mData->flModifications;
11455 alock.release();
11456
11457 if (flModifications & IsModified_SharedFolders)
11458 that->onSharedFolderChange();
11459
11460 if (flModifications & IsModified_VRDEServer)
11461 that->onVRDEServerChange(/* aRestart */ TRUE);
11462 if (flModifications & IsModified_USB)
11463 that->onUSBControllerChange();
11464
11465 for (ULONG slot = 0; slot < networkAdapters.size(); slot++)
11466 if (networkAdapters[slot])
11467 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);
11468 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++)
11469 if (serialPorts[slot])
11470 that->onSerialPortChange(serialPorts[slot]);
11471 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++)
11472 if (parallelPorts[slot])
11473 that->onParallelPortChange(parallelPorts[slot]);
11474
11475 if (flModifications & IsModified_Storage)
11476 that->onStorageControllerChange();
11477
11478#if 0
11479 if (flModifications & IsModified_BandwidthControl)
11480 that->onBandwidthControlChange();
11481#endif
11482 }
11483}
11484
11485/**
11486 * Commits all the changes to machine settings.
11487 *
11488 * Note that this operation is supposed to never fail.
11489 *
11490 * @note Locks this object and children for writing.
11491 */
11492void Machine::commit()
11493{
11494 AutoCaller autoCaller(this);
11495 AssertComRCReturnVoid(autoCaller.rc());
11496
11497 AutoCaller peerCaller(mPeer);
11498 AssertComRCReturnVoid(peerCaller.rc());
11499
11500 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11501
11502 /*
11503 * use safe commit to ensure Snapshot machines (that share mUserData)
11504 * will still refer to a valid memory location
11505 */
11506 mUserData.commitCopy();
11507
11508 mHWData.commit();
11509
11510 if (mMediaData.isBackedUp())
11511 commitMedia(Global::IsOnline(mData->mMachineState));
11512
11513 mBIOSSettings->commit();
11514 mVRDEServer->commit();
11515 mAudioAdapter->commit();
11516 mUSBController->commit();
11517 mBandwidthControl->commit();
11518
11519 /* Since mNetworkAdapters is a list which might have been changed (resized)
11520 * without using the Backupable<> template we need to handle the copying
11521 * of the list entries manually, including the creation of peers for the
11522 * new objects. */
11523 bool commitNetworkAdapters = false;
11524 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11525 if (mPeer)
11526 {
11527 /* commit everything, even the ones which will go away */
11528 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11529 mNetworkAdapters[slot]->commit();
11530 /* copy over the new entries, creating a peer and uninit the original */
11531 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11532 for (size_t slot = 0; slot < newSize; slot++)
11533 {
11534 /* look if this adapter has a peer device */
11535 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer();
11536 if (!peer)
11537 {
11538 /* no peer means the adapter is a newly created one;
11539 * create a peer owning data this data share it with */
11540 peer.createObject();
11541 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11542 }
11543 mPeer->mNetworkAdapters[slot] = peer;
11544 }
11545 /* uninit any no longer needed network adapters */
11546 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++)
11547 mNetworkAdapters[slot]->uninit();
11548 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++)
11549 {
11550 if (mPeer->mNetworkAdapters[slot])
11551 mPeer->mNetworkAdapters[slot]->uninit();
11552 }
11553 /* Keep the original network adapter count until this point, so that
11554 * discarding a chipset type change will not lose settings. */
11555 mNetworkAdapters.resize(newSize);
11556 mPeer->mNetworkAdapters.resize(newSize);
11557 }
11558 else
11559 {
11560 /* we have no peer (our parent is the newly created machine);
11561 * just commit changes to the network adapters */
11562 commitNetworkAdapters = true;
11563 }
11564 if (commitNetworkAdapters)
11565 {
11566 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11567 mNetworkAdapters[slot]->commit();
11568 }
11569
11570 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11571 mSerialPorts[slot]->commit();
11572 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11573 mParallelPorts[slot]->commit();
11574
11575 bool commitStorageControllers = false;
11576
11577 if (mStorageControllers.isBackedUp())
11578 {
11579 mStorageControllers.commit();
11580
11581 if (mPeer)
11582 {
11583 /* Commit all changes to new controllers (this will reshare data with
11584 * peers for those who have peers) */
11585 StorageControllerList *newList = new StorageControllerList();
11586 StorageControllerList::const_iterator it = mStorageControllers->begin();
11587 while (it != mStorageControllers->end())
11588 {
11589 (*it)->commit();
11590
11591 /* look if this controller has a peer device */
11592 ComObjPtr<StorageController> peer = (*it)->getPeer();
11593 if (!peer)
11594 {
11595 /* no peer means the device is a newly created one;
11596 * create a peer owning data this device share it with */
11597 peer.createObject();
11598 peer->init(mPeer, *it, true /* aReshare */);
11599 }
11600 else
11601 {
11602 /* remove peer from the old list */
11603 mPeer->mStorageControllers->remove(peer);
11604 }
11605 /* and add it to the new list */
11606 newList->push_back(peer);
11607
11608 ++it;
11609 }
11610
11611 /* uninit old peer's controllers that are left */
11612 it = mPeer->mStorageControllers->begin();
11613 while (it != mPeer->mStorageControllers->end())
11614 {
11615 (*it)->uninit();
11616 ++it;
11617 }
11618
11619 /* attach new list of controllers to our peer */
11620 mPeer->mStorageControllers.attach(newList);
11621 }
11622 else
11623 {
11624 /* we have no peer (our parent is the newly created machine);
11625 * just commit changes to devices */
11626 commitStorageControllers = true;
11627 }
11628 }
11629 else
11630 {
11631 /* the list of controllers itself is not changed,
11632 * just commit changes to controllers themselves */
11633 commitStorageControllers = true;
11634 }
11635
11636 if (commitStorageControllers)
11637 {
11638 StorageControllerList::const_iterator it = mStorageControllers->begin();
11639 while (it != mStorageControllers->end())
11640 {
11641 (*it)->commit();
11642 ++it;
11643 }
11644 }
11645
11646 if (isSessionMachine())
11647 {
11648 /* attach new data to the primary machine and reshare it */
11649 mPeer->mUserData.attach(mUserData);
11650 mPeer->mHWData.attach(mHWData);
11651 /* mMediaData is reshared by fixupMedia */
11652 // mPeer->mMediaData.attach(mMediaData);
11653 Assert(mPeer->mMediaData.data() == mMediaData.data());
11654 }
11655}
11656
11657/**
11658 * Copies all the hardware data from the given machine.
11659 *
11660 * Currently, only called when the VM is being restored from a snapshot. In
11661 * particular, this implies that the VM is not running during this method's
11662 * call.
11663 *
11664 * @note This method must be called from under this object's lock.
11665 *
11666 * @note This method doesn't call #commit(), so all data remains backed up and
11667 * unsaved.
11668 */
11669void Machine::copyFrom(Machine *aThat)
11670{
11671 AssertReturnVoid(!isSnapshotMachine());
11672 AssertReturnVoid(aThat->isSnapshotMachine());
11673
11674 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11675
11676 mHWData.assignCopy(aThat->mHWData);
11677
11678 // create copies of all shared folders (mHWData after attaching a copy
11679 // contains just references to original objects)
11680 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11681 it != mHWData->mSharedFolders.end();
11682 ++it)
11683 {
11684 ComObjPtr<SharedFolder> folder;
11685 folder.createObject();
11686 HRESULT rc = folder->initCopy(getMachine(), *it);
11687 AssertComRC(rc);
11688 *it = folder;
11689 }
11690
11691 mBIOSSettings->copyFrom(aThat->mBIOSSettings);
11692 mVRDEServer->copyFrom(aThat->mVRDEServer);
11693 mAudioAdapter->copyFrom(aThat->mAudioAdapter);
11694 mUSBController->copyFrom(aThat->mUSBController);
11695 mBandwidthControl->copyFrom(aThat->mBandwidthControl);
11696
11697 /* create private copies of all controllers */
11698 mStorageControllers.backup();
11699 mStorageControllers->clear();
11700 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
11701 it != aThat->mStorageControllers->end();
11702 ++it)
11703 {
11704 ComObjPtr<StorageController> ctrl;
11705 ctrl.createObject();
11706 ctrl->initCopy(this, *it);
11707 mStorageControllers->push_back(ctrl);
11708 }
11709
11710 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
11711 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
11712 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);
11713 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
11714 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);
11715 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
11716 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
11717}
11718
11719/**
11720 * Returns whether the given storage controller is hotplug capable.
11721 *
11722 * @returns true if the controller supports hotplugging
11723 * false otherwise.
11724 * @param enmCtrlType The controller type to check for.
11725 */
11726bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
11727{
11728 switch (enmCtrlType)
11729 {
11730 case StorageControllerType_IntelAhci:
11731 return true;
11732 case StorageControllerType_LsiLogic:
11733 case StorageControllerType_LsiLogicSas:
11734 case StorageControllerType_BusLogic:
11735 case StorageControllerType_PIIX3:
11736 case StorageControllerType_PIIX4:
11737 case StorageControllerType_ICH6:
11738 case StorageControllerType_I82078:
11739 default:
11740 return false;
11741 }
11742}
11743
11744#ifdef VBOX_WITH_RESOURCE_USAGE_API
11745
11746void Machine::getDiskList(MediaList &list)
11747{
11748 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11749 it != mMediaData->mAttachments.end();
11750 ++it)
11751 {
11752 MediumAttachment* pAttach = *it;
11753 /* just in case */
11754 AssertStmt(pAttach, continue);
11755
11756 AutoCaller localAutoCallerA(pAttach);
11757 if (FAILED(localAutoCallerA.rc())) continue;
11758
11759 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
11760
11761 if (pAttach->getType() == DeviceType_HardDisk)
11762 list.push_back(pAttach->getMedium());
11763 }
11764}
11765
11766void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
11767{
11768 AssertReturnVoid(isWriteLockOnCurrentThread());
11769 AssertPtrReturnVoid(aCollector);
11770
11771 pm::CollectorHAL *hal = aCollector->getHAL();
11772 /* Create sub metrics */
11773 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
11774 "Percentage of processor time spent in user mode by the VM process.");
11775 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
11776 "Percentage of processor time spent in kernel mode by the VM process.");
11777 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
11778 "Size of resident portion of VM process in memory.");
11779 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
11780 "Actual size of all VM disks combined.");
11781 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
11782 "Network receive rate.");
11783 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
11784 "Network transmit rate.");
11785 /* Create and register base metrics */
11786 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
11787 cpuLoadUser, cpuLoadKernel);
11788 aCollector->registerBaseMetric(cpuLoad);
11789 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
11790 ramUsageUsed);
11791 aCollector->registerBaseMetric(ramUsage);
11792 MediaList disks;
11793 getDiskList(disks);
11794 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
11795 diskUsageUsed);
11796 aCollector->registerBaseMetric(diskUsage);
11797
11798 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
11799 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11800 new pm::AggregateAvg()));
11801 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11802 new pm::AggregateMin()));
11803 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
11804 new pm::AggregateMax()));
11805 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
11806 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11807 new pm::AggregateAvg()));
11808 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11809 new pm::AggregateMin()));
11810 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
11811 new pm::AggregateMax()));
11812
11813 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
11814 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11815 new pm::AggregateAvg()));
11816 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11817 new pm::AggregateMin()));
11818 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
11819 new pm::AggregateMax()));
11820
11821 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
11822 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11823 new pm::AggregateAvg()));
11824 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11825 new pm::AggregateMin()));
11826 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
11827 new pm::AggregateMax()));
11828
11829
11830 /* Guest metrics collector */
11831 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
11832 aCollector->registerGuest(mCollectorGuest);
11833 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
11834 this, __PRETTY_FUNCTION__, mCollectorGuest));
11835
11836 /* Create sub metrics */
11837 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
11838 "Percentage of processor time spent in user mode as seen by the guest.");
11839 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
11840 "Percentage of processor time spent in kernel mode as seen by the guest.");
11841 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
11842 "Percentage of processor time spent idling as seen by the guest.");
11843
11844 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
11845 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
11846 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
11847 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
11848 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
11849 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
11850
11851 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
11852
11853 /* Create and register base metrics */
11854 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
11855 machineNetRx, machineNetTx);
11856 aCollector->registerBaseMetric(machineNetRate);
11857
11858 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
11859 guestLoadUser, guestLoadKernel, guestLoadIdle);
11860 aCollector->registerBaseMetric(guestCpuLoad);
11861
11862 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
11863 guestMemTotal, guestMemFree,
11864 guestMemBalloon, guestMemShared,
11865 guestMemCache, guestPagedTotal);
11866 aCollector->registerBaseMetric(guestCpuMem);
11867
11868 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
11869 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
11870 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
11871 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
11872
11873 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
11874 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
11875 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
11876 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
11877
11878 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
11879 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
11880 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
11881 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
11882
11883 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
11884 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
11885 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
11886 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
11887
11888 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
11889 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
11890 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
11891 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
11892
11893 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
11894 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
11895 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
11896 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
11897
11898 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
11899 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
11900 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
11901 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
11902
11903 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
11904 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
11905 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
11906 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
11907
11908 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
11909 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
11910 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
11911 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
11912
11913 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
11914 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
11915 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
11916 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
11917
11918 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
11919 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
11920 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
11921 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
11922}
11923
11924void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
11925{
11926 AssertReturnVoid(isWriteLockOnCurrentThread());
11927
11928 if (aCollector)
11929 {
11930 aCollector->unregisterMetricsFor(aMachine);
11931 aCollector->unregisterBaseMetricsFor(aMachine);
11932 }
11933}
11934
11935#endif /* VBOX_WITH_RESOURCE_USAGE_API */
11936
11937
11938////////////////////////////////////////////////////////////////////////////////
11939
11940DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
11941
11942HRESULT SessionMachine::FinalConstruct()
11943{
11944 LogFlowThisFunc(("\n"));
11945
11946#if defined(RT_OS_WINDOWS)
11947 mIPCSem = NULL;
11948#elif defined(RT_OS_OS2)
11949 mIPCSem = NULLHANDLE;
11950#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
11951 mIPCSem = -1;
11952#else
11953# error "Port me!"
11954#endif
11955
11956 return BaseFinalConstruct();
11957}
11958
11959void SessionMachine::FinalRelease()
11960{
11961 LogFlowThisFunc(("\n"));
11962
11963 uninit(Uninit::Unexpected);
11964
11965 BaseFinalRelease();
11966}
11967
11968/**
11969 * @note Must be called only by Machine::openSession() from its own write lock.
11970 */
11971HRESULT SessionMachine::init(Machine *aMachine)
11972{
11973 LogFlowThisFuncEnter();
11974 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
11975
11976 AssertReturn(aMachine, E_INVALIDARG);
11977
11978 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
11979
11980 /* Enclose the state transition NotReady->InInit->Ready */
11981 AutoInitSpan autoInitSpan(this);
11982 AssertReturn(autoInitSpan.isOk(), E_FAIL);
11983
11984 /* create the interprocess semaphore */
11985#if defined(RT_OS_WINDOWS)
11986 mIPCSemName = aMachine->mData->m_strConfigFileFull;
11987 for (size_t i = 0; i < mIPCSemName.length(); i++)
11988 if (mIPCSemName.raw()[i] == '\\')
11989 mIPCSemName.raw()[i] = '/';
11990 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw());
11991 ComAssertMsgRet(mIPCSem,
11992 ("Cannot create IPC mutex '%ls', err=%d",
11993 mIPCSemName.raw(), ::GetLastError()),
11994 E_FAIL);
11995#elif defined(RT_OS_OS2)
11996 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",
11997 aMachine->mData->mUuid.raw());
11998 mIPCSemName = ipcSem;
11999 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE);
12000 ComAssertMsgRet(arc == NO_ERROR,
12001 ("Cannot create IPC mutex '%s', arc=%ld",
12002 ipcSem.c_str(), arc),
12003 E_FAIL);
12004#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12005# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12006# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)
12007 /** @todo Check that this still works correctly. */
12008 AssertCompileSize(key_t, 8);
12009# else
12010 AssertCompileSize(key_t, 4);
12011# endif
12012 key_t key;
12013 mIPCSem = -1;
12014 mIPCKey = "0";
12015 for (uint32_t i = 0; i < 1 << 24; i++)
12016 {
12017 key = ((uint32_t)'V' << 24) | i;
12018 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);
12019 if (sem >= 0 || (errno != EEXIST && errno != EACCES))
12020 {
12021 mIPCSem = sem;
12022 if (sem >= 0)
12023 mIPCKey = BstrFmt("%u", key);
12024 break;
12025 }
12026 }
12027# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12028 Utf8Str semName = aMachine->mData->m_strConfigFileFull;
12029 char *pszSemName = NULL;
12030 RTStrUtf8ToCurrentCP(&pszSemName, semName);
12031 key_t key = ::ftok(pszSemName, 'V');
12032 RTStrFree(pszSemName);
12033
12034 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);
12035# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12036
12037 int errnoSave = errno;
12038 if (mIPCSem < 0 && errnoSave == ENOSYS)
12039 {
12040 setError(E_FAIL,
12041 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "
12042 "support for SysV IPC. Check the host kernel configuration for "
12043 "CONFIG_SYSVIPC=y"));
12044 return E_FAIL;
12045 }
12046 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing
12047 * the IPC semaphores */
12048 if (mIPCSem < 0 && errnoSave == ENOSPC)
12049 {
12050#ifdef RT_OS_LINUX
12051 setError(E_FAIL,
12052 tr("Cannot create IPC semaphore because the system limit for the "
12053 "maximum number of semaphore sets (SEMMNI), or the system wide "
12054 "maximum number of semaphores (SEMMNS) would be exceeded. The "
12055 "current set of SysV IPC semaphores can be determined from "
12056 "the file /proc/sysvipc/sem"));
12057#else
12058 setError(E_FAIL,
12059 tr("Cannot create IPC semaphore because the system-imposed limit "
12060 "on the maximum number of allowed semaphores or semaphore "
12061 "identifiers system-wide would be exceeded"));
12062#endif
12063 return E_FAIL;
12064 }
12065 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),
12066 E_FAIL);
12067 /* set the initial value to 1 */
12068 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);
12069 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),
12070 E_FAIL);
12071#else
12072# error "Port me!"
12073#endif
12074
12075 /* memorize the peer Machine */
12076 unconst(mPeer) = aMachine;
12077 /* share the parent pointer */
12078 unconst(mParent) = aMachine->mParent;
12079
12080 /* take the pointers to data to share */
12081 mData.share(aMachine->mData);
12082 mSSData.share(aMachine->mSSData);
12083
12084 mUserData.share(aMachine->mUserData);
12085 mHWData.share(aMachine->mHWData);
12086 mMediaData.share(aMachine->mMediaData);
12087
12088 mStorageControllers.allocate();
12089 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12090 it != aMachine->mStorageControllers->end();
12091 ++it)
12092 {
12093 ComObjPtr<StorageController> ctl;
12094 ctl.createObject();
12095 ctl->init(this, *it);
12096 mStorageControllers->push_back(ctl);
12097 }
12098
12099 unconst(mBIOSSettings).createObject();
12100 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12101 /* create another VRDEServer object that will be mutable */
12102 unconst(mVRDEServer).createObject();
12103 mVRDEServer->init(this, aMachine->mVRDEServer);
12104 /* create another audio adapter object that will be mutable */
12105 unconst(mAudioAdapter).createObject();
12106 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12107 /* create a list of serial ports that will be mutable */
12108 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)
12109 {
12110 unconst(mSerialPorts[slot]).createObject();
12111 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12112 }
12113 /* create a list of parallel ports that will be mutable */
12114 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)
12115 {
12116 unconst(mParallelPorts[slot]).createObject();
12117 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12118 }
12119 /* create another USB controller object that will be mutable */
12120 unconst(mUSBController).createObject();
12121 mUSBController->init(this, aMachine->mUSBController);
12122
12123 /* create a list of network adapters that will be mutable */
12124 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12125 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++)
12126 {
12127 unconst(mNetworkAdapters[slot]).createObject();
12128 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12129 }
12130
12131 /* create another bandwidth control object that will be mutable */
12132 unconst(mBandwidthControl).createObject();
12133 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12134
12135 /* default is to delete saved state on Saved -> PoweredOff transition */
12136 mRemoveSavedState = true;
12137
12138 /* Confirm a successful initialization when it's the case */
12139 autoInitSpan.setSucceeded();
12140
12141 LogFlowThisFuncLeave();
12142 return S_OK;
12143}
12144
12145/**
12146 * Uninitializes this session object. If the reason is other than
12147 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().
12148 *
12149 * @param aReason uninitialization reason
12150 *
12151 * @note Locks mParent + this object for writing.
12152 */
12153void SessionMachine::uninit(Uninit::Reason aReason)
12154{
12155 LogFlowThisFuncEnter();
12156 LogFlowThisFunc(("reason=%d\n", aReason));
12157
12158 /*
12159 * Strongly reference ourselves to prevent this object deletion after
12160 * mData->mSession.mMachine.setNull() below (which can release the last
12161 * reference and call the destructor). Important: this must be done before
12162 * accessing any members (and before AutoUninitSpan that does it as well).
12163 * This self reference will be released as the very last step on return.
12164 */
12165 ComObjPtr<SessionMachine> selfRef = this;
12166
12167 /* Enclose the state transition Ready->InUninit->NotReady */
12168 AutoUninitSpan autoUninitSpan(this);
12169 if (autoUninitSpan.uninitDone())
12170 {
12171 LogFlowThisFunc(("Already uninitialized\n"));
12172 LogFlowThisFuncLeave();
12173 return;
12174 }
12175
12176 if (autoUninitSpan.initFailed())
12177 {
12178 /* We've been called by init() because it's failed. It's not really
12179 * necessary (nor it's safe) to perform the regular uninit sequence
12180 * below, the following is enough.
12181 */
12182 LogFlowThisFunc(("Initialization failed.\n"));
12183#if defined(RT_OS_WINDOWS)
12184 if (mIPCSem)
12185 ::CloseHandle(mIPCSem);
12186 mIPCSem = NULL;
12187#elif defined(RT_OS_OS2)
12188 if (mIPCSem != NULLHANDLE)
12189 ::DosCloseMutexSem(mIPCSem);
12190 mIPCSem = NULLHANDLE;
12191#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12192 if (mIPCSem >= 0)
12193 ::semctl(mIPCSem, 0, IPC_RMID);
12194 mIPCSem = -1;
12195# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12196 mIPCKey = "0";
12197# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12198#else
12199# error "Port me!"
12200#endif
12201 uninitDataAndChildObjects();
12202 mData.free();
12203 unconst(mParent) = NULL;
12204 unconst(mPeer) = NULL;
12205 LogFlowThisFuncLeave();
12206 return;
12207 }
12208
12209 MachineState_T lastState;
12210 {
12211 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12212 lastState = mData->mMachineState;
12213 }
12214 NOREF(lastState);
12215
12216#ifdef VBOX_WITH_USB
12217 // release all captured USB devices, but do this before requesting the locks below
12218 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12219 {
12220 /* Console::captureUSBDevices() is called in the VM process only after
12221 * setting the machine state to Starting or Restoring.
12222 * Console::detachAllUSBDevices() will be called upon successful
12223 * termination. So, we need to release USB devices only if there was
12224 * an abnormal termination of a running VM.
12225 *
12226 * This is identical to SessionMachine::DetachAllUSBDevices except
12227 * for the aAbnormal argument. */
12228 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12229 AssertComRC(rc);
12230 NOREF(rc);
12231
12232 USBProxyService *service = mParent->host()->usbProxyService();
12233 if (service)
12234 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12235 }
12236#endif /* VBOX_WITH_USB */
12237
12238 // we need to lock this object in uninit() because the lock is shared
12239 // with mPeer (as well as data we modify below). mParent->addProcessToReap()
12240 // and others need mParent lock, and USB needs host lock.
12241 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
12242
12243#if 0
12244 // Trigger async cleanup tasks, avoid doing things here which are not
12245 // vital to be done immediately and maybe need more locks. This calls
12246 // Machine::unregisterMetrics().
12247 mParent->onMachineUninit(mPeer);
12248#else
12249 /*
12250 * It is safe to call Machine::unregisterMetrics() here because
12251 * PerformanceCollector::samplerCallback no longer accesses guest methods
12252 * holding the lock.
12253 */
12254 unregisterMetrics(mParent->performanceCollector(), mPeer);
12255#endif
12256 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12257 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
12258 this, __PRETTY_FUNCTION__, mCollectorGuest));
12259 if (mCollectorGuest)
12260 {
12261 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
12262 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12263 mCollectorGuest = NULL;
12264 }
12265
12266 if (aReason == Uninit::Abnormal)
12267 {
12268 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",
12269 Global::IsOnlineOrTransient(lastState)));
12270
12271 /* reset the state to Aborted */
12272 if (mData->mMachineState != MachineState_Aborted)
12273 setMachineState(MachineState_Aborted);
12274 }
12275
12276 // any machine settings modified?
12277 if (mData->flModifications)
12278 {
12279 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));
12280 rollback(false /* aNotify */);
12281 }
12282
12283 Assert( mConsoleTaskData.strStateFilePath.isEmpty()
12284 || !mConsoleTaskData.mSnapshot);
12285 if (!mConsoleTaskData.strStateFilePath.isEmpty())
12286 {
12287 LogWarningThisFunc(("canceling failed save state request!\n"));
12288 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
12289 }
12290 else if (!mConsoleTaskData.mSnapshot.isNull())
12291 {
12292 LogWarningThisFunc(("canceling untaken snapshot!\n"));
12293
12294 /* delete all differencing hard disks created (this will also attach
12295 * their parents back by rolling back mMediaData) */
12296 rollbackMedia();
12297
12298 // delete the saved state file (it might have been already created)
12299 // AFTER killing the snapshot so that releaseSavedStateFile() won't
12300 // think it's still in use
12301 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
12302 mConsoleTaskData.mSnapshot->uninit();
12303 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
12304 }
12305
12306 if (!mData->mSession.mType.isEmpty())
12307 {
12308 /* mType is not null when this machine's process has been started by
12309 * Machine::LaunchVMProcess(), therefore it is our child. We
12310 * need to queue the PID to reap the process (and avoid zombies on
12311 * Linux). */
12312 Assert(mData->mSession.mPID != NIL_RTPROCESS);
12313 mParent->addProcessToReap(mData->mSession.mPID);
12314 }
12315
12316 mData->mSession.mPID = NIL_RTPROCESS;
12317
12318 if (aReason == Uninit::Unexpected)
12319 {
12320 /* Uninitialization didn't come from #checkForDeath(), so tell the
12321 * client watcher thread to update the set of machines that have open
12322 * sessions. */
12323 mParent->updateClientWatcher();
12324 }
12325
12326 /* uninitialize all remote controls */
12327 if (mData->mSession.mRemoteControls.size())
12328 {
12329 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12330 mData->mSession.mRemoteControls.size()));
12331
12332 Data::Session::RemoteControlList::iterator it =
12333 mData->mSession.mRemoteControls.begin();
12334 while (it != mData->mSession.mRemoteControls.end())
12335 {
12336 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12337 HRESULT rc = (*it)->Uninitialize();
12338 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12339 if (FAILED(rc))
12340 LogWarningThisFunc(("Forgot to close the remote session?\n"));
12341 ++it;
12342 }
12343 mData->mSession.mRemoteControls.clear();
12344 }
12345
12346 /*
12347 * An expected uninitialization can come only from #checkForDeath().
12348 * Otherwise it means that something's gone really wrong (for example,
12349 * the Session implementation has released the VirtualBox reference
12350 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12351 * etc). However, it's also possible, that the client releases the IPC
12352 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12353 * but the VirtualBox release event comes first to the server process.
12354 * This case is practically possible, so we should not assert on an
12355 * unexpected uninit, just log a warning.
12356 */
12357
12358 if ((aReason == Uninit::Unexpected))
12359 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12360
12361 if (aReason != Uninit::Normal)
12362 {
12363 mData->mSession.mDirectControl.setNull();
12364 }
12365 else
12366 {
12367 /* this must be null here (see #OnSessionEnd()) */
12368 Assert(mData->mSession.mDirectControl.isNull());
12369 Assert(mData->mSession.mState == SessionState_Unlocking);
12370 Assert(!mData->mSession.mProgress.isNull());
12371 }
12372 if (mData->mSession.mProgress)
12373 {
12374 if (aReason == Uninit::Normal)
12375 mData->mSession.mProgress->notifyComplete(S_OK);
12376 else
12377 mData->mSession.mProgress->notifyComplete(E_FAIL,
12378 COM_IIDOF(ISession),
12379 getComponentName(),
12380 tr("The VM session was aborted"));
12381 mData->mSession.mProgress.setNull();
12382 }
12383
12384 /* remove the association between the peer machine and this session machine */
12385 Assert( (SessionMachine*)mData->mSession.mMachine == this
12386 || aReason == Uninit::Unexpected);
12387
12388 /* reset the rest of session data */
12389 mData->mSession.mMachine.setNull();
12390 mData->mSession.mState = SessionState_Unlocked;
12391 mData->mSession.mType.setNull();
12392
12393 /* close the interprocess semaphore before leaving the exclusive lock */
12394#if defined(RT_OS_WINDOWS)
12395 if (mIPCSem)
12396 ::CloseHandle(mIPCSem);
12397 mIPCSem = NULL;
12398#elif defined(RT_OS_OS2)
12399 if (mIPCSem != NULLHANDLE)
12400 ::DosCloseMutexSem(mIPCSem);
12401 mIPCSem = NULLHANDLE;
12402#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12403 if (mIPCSem >= 0)
12404 ::semctl(mIPCSem, 0, IPC_RMID);
12405 mIPCSem = -1;
12406# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12407 mIPCKey = "0";
12408# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */
12409#else
12410# error "Port me!"
12411#endif
12412
12413 /* fire an event */
12414 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12415
12416 uninitDataAndChildObjects();
12417
12418 /* free the essential data structure last */
12419 mData.free();
12420
12421 /* release the exclusive lock before setting the below two to NULL */
12422 multilock.release();
12423
12424 unconst(mParent) = NULL;
12425 unconst(mPeer) = NULL;
12426
12427 LogFlowThisFuncLeave();
12428}
12429
12430// util::Lockable interface
12431////////////////////////////////////////////////////////////////////////////////
12432
12433/**
12434 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12435 * with the primary Machine instance (mPeer).
12436 */
12437RWLockHandle *SessionMachine::lockHandle() const
12438{
12439 AssertReturn(mPeer != NULL, NULL);
12440 return mPeer->lockHandle();
12441}
12442
12443// IInternalMachineControl methods
12444////////////////////////////////////////////////////////////////////////////////
12445
12446/**
12447 * Passes collected guest statistics to performance collector object
12448 */
12449STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12450 ULONG aCpuKernel, ULONG aCpuIdle,
12451 ULONG aMemTotal, ULONG aMemFree,
12452 ULONG aMemBalloon, ULONG aMemShared,
12453 ULONG aMemCache, ULONG aPageTotal,
12454 ULONG aAllocVMM, ULONG aFreeVMM,
12455 ULONG aBalloonedVMM, ULONG aSharedVMM,
12456 ULONG aVmNetRx, ULONG aVmNetTx)
12457{
12458 if (mCollectorGuest)
12459 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12460 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12461 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12462 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12463
12464 return S_OK;
12465}
12466
12467/**
12468 * @note Locks this object for writing.
12469 */
12470STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)
12471{
12472 AutoCaller autoCaller(this);
12473 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12474
12475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12476
12477 mRemoveSavedState = aRemove;
12478
12479 return S_OK;
12480}
12481
12482/**
12483 * @note Locks the same as #setMachineState() does.
12484 */
12485STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)
12486{
12487 return setMachineState(aMachineState);
12488}
12489
12490/**
12491 * @note Locks this object for reading.
12492 */
12493STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)
12494{
12495 AutoCaller autoCaller(this);
12496 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12497
12498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
12499
12500#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
12501 mIPCSemName.cloneTo(aId);
12502 return S_OK;
12503#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
12504# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN
12505 mIPCKey.cloneTo(aId);
12506# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12507 mData->m_strConfigFileFull.cloneTo(aId);
12508# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */
12509 return S_OK;
12510#else
12511# error "Port me!"
12512#endif
12513}
12514
12515/**
12516 * @note Locks this object for writing.
12517 */
12518STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)
12519{
12520 LogFlowThisFunc(("aProgress=%p\n", aProgress));
12521 AutoCaller autoCaller(this);
12522 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12523
12524 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12525
12526 if (mData->mSession.mState != SessionState_Locked)
12527 return VBOX_E_INVALID_OBJECT_STATE;
12528
12529 if (!mData->mSession.mProgress.isNull())
12530 mData->mSession.mProgress->setOtherProgressObject(aProgress);
12531
12532 LogFlowThisFunc(("returns S_OK.\n"));
12533 return S_OK;
12534}
12535
12536/**
12537 * @note Locks this object for writing.
12538 */
12539STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)
12540{
12541 AutoCaller autoCaller(this);
12542 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12543
12544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12545
12546 if (mData->mSession.mState != SessionState_Locked)
12547 return VBOX_E_INVALID_OBJECT_STATE;
12548
12549 /* Finalize the LaunchVMProcess progress object. */
12550 if (mData->mSession.mProgress)
12551 {
12552 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);
12553 mData->mSession.mProgress.setNull();
12554 }
12555
12556 if (SUCCEEDED((HRESULT)iResult))
12557 {
12558#ifdef VBOX_WITH_RESOURCE_USAGE_API
12559 /* The VM has been powered up successfully, so it makes sense
12560 * now to offer the performance metrics for a running machine
12561 * object. Doing it earlier wouldn't be safe. */
12562 registerMetrics(mParent->performanceCollector(), mPeer,
12563 mData->mSession.mPID);
12564#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12565 }
12566
12567 return S_OK;
12568}
12569
12570/**
12571 * @note Locks this object for writing.
12572 */
12573STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)
12574{
12575 LogFlowThisFuncEnter();
12576
12577 CheckComArgOutPointerValid(aProgress);
12578
12579 AutoCaller autoCaller(this);
12580 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12581
12582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12583
12584 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
12585 E_FAIL);
12586
12587 /* create a progress object to track operation completion */
12588 ComObjPtr<Progress> pProgress;
12589 pProgress.createObject();
12590 pProgress->init(getVirtualBox(),
12591 static_cast<IMachine *>(this) /* aInitiator */,
12592 Bstr(tr("Stopping the virtual machine")).raw(),
12593 FALSE /* aCancelable */);
12594
12595 /* fill in the console task data */
12596 mConsoleTaskData.mLastState = mData->mMachineState;
12597 mConsoleTaskData.mProgress = pProgress;
12598
12599 /* set the state to Stopping (this is expected by Console::PowerDown()) */
12600 setMachineState(MachineState_Stopping);
12601
12602 pProgress.queryInterfaceTo(aProgress);
12603
12604 return S_OK;
12605}
12606
12607/**
12608 * @note Locks this object for writing.
12609 */
12610STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)
12611{
12612 LogFlowThisFuncEnter();
12613
12614 AutoCaller autoCaller(this);
12615 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12616
12617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12618
12619 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)
12620 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))
12621 && mConsoleTaskData.mLastState != MachineState_Null,
12622 E_FAIL);
12623
12624 /*
12625 * On failure, set the state to the state we had when BeginPoweringDown()
12626 * was called (this is expected by Console::PowerDown() and the associated
12627 * task). On success the VM process already changed the state to
12628 * MachineState_PoweredOff, so no need to do anything.
12629 */
12630 if (FAILED(iResult))
12631 setMachineState(mConsoleTaskData.mLastState);
12632
12633 /* notify the progress object about operation completion */
12634 Assert(mConsoleTaskData.mProgress);
12635 if (SUCCEEDED(iResult))
12636 mConsoleTaskData.mProgress->notifyComplete(S_OK);
12637 else
12638 {
12639 Utf8Str strErrMsg(aErrMsg);
12640 if (strErrMsg.length())
12641 mConsoleTaskData.mProgress->notifyComplete(iResult,
12642 COM_IIDOF(ISession),
12643 getComponentName(),
12644 strErrMsg.c_str());
12645 else
12646 mConsoleTaskData.mProgress->notifyComplete(iResult);
12647 }
12648
12649 /* clear out the temporary saved state data */
12650 mConsoleTaskData.mLastState = MachineState_Null;
12651 mConsoleTaskData.mProgress.setNull();
12652
12653 LogFlowThisFuncLeave();
12654 return S_OK;
12655}
12656
12657
12658/**
12659 * Goes through the USB filters of the given machine to see if the given
12660 * device matches any filter or not.
12661 *
12662 * @note Locks the same as USBController::hasMatchingFilter() does.
12663 */
12664STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,
12665 BOOL *aMatched,
12666 ULONG *aMaskedIfs)
12667{
12668 LogFlowThisFunc(("\n"));
12669
12670 CheckComArgNotNull(aUSBDevice);
12671 CheckComArgOutPointerValid(aMatched);
12672
12673 AutoCaller autoCaller(this);
12674 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12675
12676#ifdef VBOX_WITH_USB
12677 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);
12678#else
12679 NOREF(aUSBDevice);
12680 NOREF(aMaskedIfs);
12681 *aMatched = FALSE;
12682#endif
12683
12684 return S_OK;
12685}
12686
12687/**
12688 * @note Locks the same as Host::captureUSBDevice() does.
12689 */
12690STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)
12691{
12692 LogFlowThisFunc(("\n"));
12693
12694 AutoCaller autoCaller(this);
12695 AssertComRCReturnRC(autoCaller.rc());
12696
12697#ifdef VBOX_WITH_USB
12698 /* if captureDeviceForVM() fails, it must have set extended error info */
12699 clearError();
12700 MultiResult rc = mParent->host()->checkUSBProxyService();
12701 if (FAILED(rc)) return rc;
12702
12703 USBProxyService *service = mParent->host()->usbProxyService();
12704 AssertReturn(service, E_FAIL);
12705 return service->captureDeviceForVM(this, Guid(aId).ref());
12706#else
12707 NOREF(aId);
12708 return E_NOTIMPL;
12709#endif
12710}
12711
12712/**
12713 * @note Locks the same as Host::detachUSBDevice() does.
12714 */
12715STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)
12716{
12717 LogFlowThisFunc(("\n"));
12718
12719 AutoCaller autoCaller(this);
12720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12721
12722#ifdef VBOX_WITH_USB
12723 USBProxyService *service = mParent->host()->usbProxyService();
12724 AssertReturn(service, E_FAIL);
12725 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);
12726#else
12727 NOREF(aId);
12728 NOREF(aDone);
12729 return E_NOTIMPL;
12730#endif
12731}
12732
12733/**
12734 * Inserts all machine filters to the USB proxy service and then calls
12735 * Host::autoCaptureUSBDevices().
12736 *
12737 * Called by Console from the VM process upon VM startup.
12738 *
12739 * @note Locks what called methods lock.
12740 */
12741STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()
12742{
12743 LogFlowThisFunc(("\n"));
12744
12745 AutoCaller autoCaller(this);
12746 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12747
12748#ifdef VBOX_WITH_USB
12749 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);
12750 AssertComRC(rc);
12751 NOREF(rc);
12752
12753 USBProxyService *service = mParent->host()->usbProxyService();
12754 AssertReturn(service, E_FAIL);
12755 return service->autoCaptureDevicesForVM(this);
12756#else
12757 return S_OK;
12758#endif
12759}
12760
12761/**
12762 * Removes all machine filters from the USB proxy service and then calls
12763 * Host::detachAllUSBDevices().
12764 *
12765 * Called by Console from the VM process upon normal VM termination or by
12766 * SessionMachine::uninit() upon abnormal VM termination (from under the
12767 * Machine/SessionMachine lock).
12768 *
12769 * @note Locks what called methods lock.
12770 */
12771STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)
12772{
12773 LogFlowThisFunc(("\n"));
12774
12775 AutoCaller autoCaller(this);
12776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12777
12778#ifdef VBOX_WITH_USB
12779 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);
12780 AssertComRC(rc);
12781 NOREF(rc);
12782
12783 USBProxyService *service = mParent->host()->usbProxyService();
12784 AssertReturn(service, E_FAIL);
12785 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
12786#else
12787 NOREF(aDone);
12788 return S_OK;
12789#endif
12790}
12791
12792/**
12793 * @note Locks this object for writing.
12794 */
12795STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,
12796 IProgress **aProgress)
12797{
12798 LogFlowThisFuncEnter();
12799
12800 AssertReturn(aSession, E_INVALIDARG);
12801 AssertReturn(aProgress, E_INVALIDARG);
12802
12803 AutoCaller autoCaller(this);
12804
12805 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));
12806 /*
12807 * We don't assert below because it might happen that a non-direct session
12808 * informs us it is closed right after we've been uninitialized -- it's ok.
12809 */
12810 if (FAILED(autoCaller.rc())) return autoCaller.rc();
12811
12812 /* get IInternalSessionControl interface */
12813 ComPtr<IInternalSessionControl> control(aSession);
12814
12815 ComAssertRet(!control.isNull(), E_INVALIDARG);
12816
12817 /* Creating a Progress object requires the VirtualBox lock, and
12818 * thus locking it here is required by the lock order rules. */
12819 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12820
12821 if (control == mData->mSession.mDirectControl)
12822 {
12823 ComAssertRet(aProgress, E_POINTER);
12824
12825 /* The direct session is being normally closed by the client process
12826 * ----------------------------------------------------------------- */
12827
12828 /* go to the closing state (essential for all open*Session() calls and
12829 * for #checkForDeath()) */
12830 Assert(mData->mSession.mState == SessionState_Locked);
12831 mData->mSession.mState = SessionState_Unlocking;
12832
12833 /* set direct control to NULL to release the remote instance */
12834 mData->mSession.mDirectControl.setNull();
12835 LogFlowThisFunc(("Direct control is set to NULL\n"));
12836
12837 if (mData->mSession.mProgress)
12838 {
12839 /* finalize the progress, someone might wait if a frontend
12840 * closes the session before powering on the VM. */
12841 mData->mSession.mProgress->notifyComplete(E_FAIL,
12842 COM_IIDOF(ISession),
12843 getComponentName(),
12844 tr("The VM session was closed before any attempt to power it on"));
12845 mData->mSession.mProgress.setNull();
12846 }
12847
12848 /* Create the progress object the client will use to wait until
12849 * #checkForDeath() is called to uninitialize this session object after
12850 * it releases the IPC semaphore.
12851 * Note! Because we're "reusing" mProgress here, this must be a proxy
12852 * object just like for LaunchVMProcess. */
12853 Assert(mData->mSession.mProgress.isNull());
12854 ComObjPtr<ProgressProxy> progress;
12855 progress.createObject();
12856 ComPtr<IUnknown> pPeer(mPeer);
12857 progress->init(mParent, pPeer,
12858 Bstr(tr("Closing session")).raw(),
12859 FALSE /* aCancelable */);
12860 progress.queryInterfaceTo(aProgress);
12861 mData->mSession.mProgress = progress;
12862 }
12863 else
12864 {
12865 /* the remote session is being normally closed */
12866 Data::Session::RemoteControlList::iterator it =
12867 mData->mSession.mRemoteControls.begin();
12868 while (it != mData->mSession.mRemoteControls.end())
12869 {
12870 if (control == *it)
12871 break;
12872 ++it;
12873 }
12874 BOOL found = it != mData->mSession.mRemoteControls.end();
12875 ComAssertMsgRet(found, ("The session is not found in the session list!"),
12876 E_INVALIDARG);
12877 // This MUST be erase(it), not remove(*it) as the latter triggers a
12878 // very nasty use after free due to the place where the value "lives".
12879 mData->mSession.mRemoteControls.erase(it);
12880 }
12881
12882 /* signal the client watcher thread, because the client is going away */
12883 mParent->updateClientWatcher();
12884
12885 LogFlowThisFuncLeave();
12886 return S_OK;
12887}
12888
12889/**
12890 * @note Locks this object for writing.
12891 */
12892STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)
12893{
12894 LogFlowThisFuncEnter();
12895
12896 CheckComArgOutPointerValid(aProgress);
12897 CheckComArgOutPointerValid(aStateFilePath);
12898
12899 AutoCaller autoCaller(this);
12900 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12901
12902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12903
12904 AssertReturn( mData->mMachineState == MachineState_Paused
12905 && mConsoleTaskData.mLastState == MachineState_Null
12906 && mConsoleTaskData.strStateFilePath.isEmpty(),
12907 E_FAIL);
12908
12909 /* create a progress object to track operation completion */
12910 ComObjPtr<Progress> pProgress;
12911 pProgress.createObject();
12912 pProgress->init(getVirtualBox(),
12913 static_cast<IMachine *>(this) /* aInitiator */,
12914 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12915 FALSE /* aCancelable */);
12916
12917 Utf8Str strStateFilePath;
12918 /* stateFilePath is null when the machine is not running */
12919 if (mData->mMachineState == MachineState_Paused)
12920 composeSavedStateFilename(strStateFilePath);
12921
12922 /* fill in the console task data */
12923 mConsoleTaskData.mLastState = mData->mMachineState;
12924 mConsoleTaskData.strStateFilePath = strStateFilePath;
12925 mConsoleTaskData.mProgress = pProgress;
12926
12927 /* set the state to Saving (this is expected by Console::SaveState()) */
12928 setMachineState(MachineState_Saving);
12929
12930 strStateFilePath.cloneTo(aStateFilePath);
12931 pProgress.queryInterfaceTo(aProgress);
12932
12933 return S_OK;
12934}
12935
12936/**
12937 * @note Locks mParent + this object for writing.
12938 */
12939STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
12940{
12941 LogFlowThisFunc(("\n"));
12942
12943 AutoCaller autoCaller(this);
12944 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12945
12946 /* endSavingState() need mParent lock */
12947 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
12948
12949 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
12950 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
12951 && mConsoleTaskData.mLastState != MachineState_Null
12952 && !mConsoleTaskData.strStateFilePath.isEmpty(),
12953 E_FAIL);
12954
12955 /*
12956 * On failure, set the state to the state we had when BeginSavingState()
12957 * was called (this is expected by Console::SaveState() and the associated
12958 * task). On success the VM process already changed the state to
12959 * MachineState_Saved, so no need to do anything.
12960 */
12961 if (FAILED(iResult))
12962 setMachineState(mConsoleTaskData.mLastState);
12963
12964 return endSavingState(iResult, aErrMsg);
12965}
12966
12967/**
12968 * @note Locks this object for writing.
12969 */
12970STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
12971{
12972 LogFlowThisFunc(("\n"));
12973
12974 CheckComArgStrNotEmptyOrNull(aSavedStateFile);
12975
12976 AutoCaller autoCaller(this);
12977 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
12978
12979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12980
12981 AssertReturn( mData->mMachineState == MachineState_PoweredOff
12982 || mData->mMachineState == MachineState_Teleported
12983 || mData->mMachineState == MachineState_Aborted
12984 , E_FAIL); /** @todo setError. */
12985
12986 Utf8Str stateFilePathFull = aSavedStateFile;
12987 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);
12988 if (RT_FAILURE(vrc))
12989 return setError(VBOX_E_FILE_ERROR,
12990 tr("Invalid saved state file path '%ls' (%Rrc)"),
12991 aSavedStateFile,
12992 vrc);
12993
12994 mSSData->strStateFilePath = stateFilePathFull;
12995
12996 /* The below setMachineState() will detect the state transition and will
12997 * update the settings file */
12998
12999 return setMachineState(MachineState_Saved);
13000}
13001
13002STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),
13003 ComSafeArrayOut(BSTR, aValues),
13004 ComSafeArrayOut(LONG64, aTimestamps),
13005 ComSafeArrayOut(BSTR, aFlags))
13006{
13007 LogFlowThisFunc(("\n"));
13008
13009#ifdef VBOX_WITH_GUEST_PROPS
13010 using namespace guestProp;
13011
13012 AutoCaller autoCaller(this);
13013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13014
13015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13016
13017 CheckComArgOutSafeArrayPointerValid(aNames);
13018 CheckComArgOutSafeArrayPointerValid(aValues);
13019 CheckComArgOutSafeArrayPointerValid(aTimestamps);
13020 CheckComArgOutSafeArrayPointerValid(aFlags);
13021
13022 size_t cEntries = mHWData->mGuestProperties.size();
13023 com::SafeArray<BSTR> names(cEntries);
13024 com::SafeArray<BSTR> values(cEntries);
13025 com::SafeArray<LONG64> timestamps(cEntries);
13026 com::SafeArray<BSTR> flags(cEntries);
13027 unsigned i = 0;
13028 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13029 it != mHWData->mGuestProperties.end();
13030 ++it)
13031 {
13032 char szFlags[MAX_FLAGS_LEN + 1];
13033 it->first.cloneTo(&names[i]);
13034 it->second.strValue.cloneTo(&values[i]);
13035 timestamps[i] = it->second.mTimestamp;
13036 /* If it is NULL, keep it NULL. */
13037 if (it->second.mFlags)
13038 {
13039 writeFlags(it->second.mFlags, szFlags);
13040 Bstr(szFlags).cloneTo(&flags[i]);
13041 }
13042 else
13043 flags[i] = NULL;
13044 ++i;
13045 }
13046 names.detachTo(ComSafeArrayOutArg(aNames));
13047 values.detachTo(ComSafeArrayOutArg(aValues));
13048 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));
13049 flags.detachTo(ComSafeArrayOutArg(aFlags));
13050 return S_OK;
13051#else
13052 ReturnComNotImplemented();
13053#endif
13054}
13055
13056STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
13057 IN_BSTR aValue,
13058 LONG64 aTimestamp,
13059 IN_BSTR aFlags)
13060{
13061 LogFlowThisFunc(("\n"));
13062
13063#ifdef VBOX_WITH_GUEST_PROPS
13064 using namespace guestProp;
13065
13066 CheckComArgStrNotEmptyOrNull(aName);
13067 CheckComArgNotNull(aValue);
13068 CheckComArgNotNull(aFlags);
13069
13070 try
13071 {
13072 /*
13073 * Convert input up front.
13074 */
13075 Utf8Str utf8Name(aName);
13076 uint32_t fFlags = NILFLAG;
13077 if (aFlags)
13078 {
13079 Utf8Str utf8Flags(aFlags);
13080 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);
13081 AssertRCReturn(vrc, E_INVALIDARG);
13082 }
13083
13084 /*
13085 * Now grab the object lock, validate the state and do the update.
13086 */
13087 AutoCaller autoCaller(this);
13088 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13089
13090 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13091
13092 switch (mData->mMachineState)
13093 {
13094 case MachineState_Paused:
13095 case MachineState_Running:
13096 case MachineState_Teleporting:
13097 case MachineState_TeleportingPausedVM:
13098 case MachineState_LiveSnapshotting:
13099 case MachineState_DeletingSnapshotOnline:
13100 case MachineState_DeletingSnapshotPaused:
13101 case MachineState_Saving:
13102 case MachineState_Stopping:
13103 break;
13104
13105 default:
13106 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13107 VBOX_E_INVALID_VM_STATE);
13108 }
13109
13110 setModified(IsModified_MachineData);
13111 mHWData.backup();
13112
13113 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);
13114 if (it != mHWData->mGuestProperties.end())
13115 {
13116 if (RT_VALID_PTR(aValue) && *(aValue) != '\0')
13117 {
13118 it->second.strValue = aValue;
13119 it->second.mFlags = fFlags;
13120 it->second.mTimestamp = aTimestamp;
13121 }
13122 else
13123 mHWData->mGuestProperties.erase(it);
13124
13125 mData->mGuestPropertiesModified = TRUE;
13126 }
13127
13128 /*
13129 * Send a callback notification if appropriate
13130 */
13131 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()
13132 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),
13133 RTSTR_MAX,
13134 utf8Name.c_str(),
13135 RTSTR_MAX, NULL)
13136 )
13137 {
13138 alock.release();
13139
13140 mParent->onGuestPropertyChange(mData->mUuid,
13141 aName,
13142 aValue,
13143 aFlags);
13144 }
13145 }
13146 catch (...)
13147 {
13148 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13149 }
13150 return S_OK;
13151#else
13152 ReturnComNotImplemented();
13153#endif
13154}
13155
13156STDMETHODIMP SessionMachine::LockMedia()
13157{
13158 AutoCaller autoCaller(this);
13159 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13160
13161 AutoMultiWriteLock2 alock(this->lockHandle(),
13162 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13163
13164 AssertReturn( mData->mMachineState == MachineState_Starting
13165 || mData->mMachineState == MachineState_Restoring
13166 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13167
13168 clearError();
13169 alock.release();
13170 return lockMedia();
13171}
13172
13173STDMETHODIMP SessionMachine::UnlockMedia()
13174{
13175 unlockMedia();
13176 return S_OK;
13177}
13178
13179STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
13180 IMediumAttachment **aNewAttachment)
13181{
13182 CheckComArgNotNull(aAttachment);
13183 CheckComArgOutPointerValid(aNewAttachment);
13184
13185 AutoCaller autoCaller(this);
13186 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13187
13188 // request the host lock first, since might be calling Host methods for getting host drives;
13189 // next, protect the media tree all the while we're in here, as well as our member variables
13190 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
13191 this->lockHandle(),
13192 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13193
13194 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
13195
13196 Bstr ctrlName;
13197 LONG lPort;
13198 LONG lDevice;
13199 bool fTempEject;
13200 {
13201 AutoCaller autoAttachCaller(this);
13202 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13203
13204 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13205
13206 /* Need to query the details first, as the IMediumAttachment reference
13207 * might be to the original settings, which we are going to change. */
13208 ctrlName = pAttach->getControllerName();
13209 lPort = pAttach->getPort();
13210 lDevice = pAttach->getDevice();
13211 fTempEject = pAttach->getTempEject();
13212 }
13213
13214 if (!fTempEject)
13215 {
13216 /* Remember previously mounted medium. The medium before taking the
13217 * backup is not necessarily the same thing. */
13218 ComObjPtr<Medium> oldmedium;
13219 oldmedium = pAttach->getMedium();
13220
13221 setModified(IsModified_Storage);
13222 mMediaData.backup();
13223
13224 // The backup operation makes the pAttach reference point to the
13225 // old settings. Re-get the correct reference.
13226 pAttach = findAttachment(mMediaData->mAttachments,
13227 ctrlName.raw(),
13228 lPort,
13229 lDevice);
13230
13231 {
13232 AutoCaller autoAttachCaller(this);
13233 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13234
13235 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13236 if (!oldmedium.isNull())
13237 oldmedium->removeBackReference(mData->mUuid);
13238
13239 pAttach->updateMedium(NULL);
13240 pAttach->updateEjected();
13241 }
13242
13243 setModified(IsModified_Storage);
13244 }
13245 else
13246 {
13247 {
13248 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13249 pAttach->updateEjected();
13250 }
13251 }
13252
13253 pAttach.queryInterfaceTo(aNewAttachment);
13254
13255 return S_OK;
13256}
13257
13258// public methods only for internal purposes
13259/////////////////////////////////////////////////////////////////////////////
13260
13261/**
13262 * Called from the client watcher thread to check for expected or unexpected
13263 * death of the client process that has a direct session to this machine.
13264 *
13265 * On Win32 and on OS/2, this method is called only when we've got the
13266 * mutex (i.e. the client has either died or terminated normally) so it always
13267 * returns @c true (the client is terminated, the session machine is
13268 * uninitialized).
13269 *
13270 * On other platforms, the method returns @c true if the client process has
13271 * terminated normally or abnormally and the session machine was uninitialized,
13272 * and @c false if the client process is still alive.
13273 *
13274 * @note Locks this object for writing.
13275 */
13276bool SessionMachine::checkForDeath()
13277{
13278 Uninit::Reason reason;
13279 bool terminated = false;
13280
13281 /* Enclose autoCaller with a block because calling uninit() from under it
13282 * will deadlock. */
13283 {
13284 AutoCaller autoCaller(this);
13285 if (!autoCaller.isOk())
13286 {
13287 /* return true if not ready, to cause the client watcher to exclude
13288 * the corresponding session from watching */
13289 LogFlowThisFunc(("Already uninitialized!\n"));
13290 return true;
13291 }
13292
13293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13294
13295 /* Determine the reason of death: if the session state is Closing here,
13296 * everything is fine. Otherwise it means that the client did not call
13297 * OnSessionEnd() before it released the IPC semaphore. This may happen
13298 * either because the client process has abnormally terminated, or
13299 * because it simply forgot to call ISession::Close() before exiting. We
13300 * threat the latter also as an abnormal termination (see
13301 * Session::uninit() for details). */
13302 reason = mData->mSession.mState == SessionState_Unlocking ?
13303 Uninit::Normal :
13304 Uninit::Abnormal;
13305
13306#if defined(RT_OS_WINDOWS)
13307
13308 AssertMsg(mIPCSem, ("semaphore must be created"));
13309
13310 /* release the IPC mutex */
13311 ::ReleaseMutex(mIPCSem);
13312
13313 terminated = true;
13314
13315#elif defined(RT_OS_OS2)
13316
13317 AssertMsg(mIPCSem, ("semaphore must be created"));
13318
13319 /* release the IPC mutex */
13320 ::DosReleaseMutexSem(mIPCSem);
13321
13322 terminated = true;
13323
13324#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)
13325
13326 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));
13327
13328 int val = ::semctl(mIPCSem, 0, GETVAL);
13329 if (val > 0)
13330 {
13331 /* the semaphore is signaled, meaning the session is terminated */
13332 terminated = true;
13333 }
13334
13335#else
13336# error "Port me!"
13337#endif
13338
13339 } /* AutoCaller block */
13340
13341 if (terminated)
13342 uninit(reason);
13343
13344 return terminated;
13345}
13346
13347/**
13348 * @note Locks this object for reading.
13349 */
13350HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13351{
13352 LogFlowThisFunc(("\n"));
13353
13354 AutoCaller autoCaller(this);
13355 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13356
13357 ComPtr<IInternalSessionControl> directControl;
13358 {
13359 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13360 directControl = mData->mSession.mDirectControl;
13361 }
13362
13363 /* ignore notifications sent after #OnSessionEnd() is called */
13364 if (!directControl)
13365 return S_OK;
13366
13367 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13368}
13369
13370/**
13371 * @note Locks this object for reading.
13372 */
13373HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13374 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort)
13375{
13376 LogFlowThisFunc(("\n"));
13377
13378 AutoCaller autoCaller(this);
13379 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13380
13381 ComPtr<IInternalSessionControl> directControl;
13382 {
13383 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13384 directControl = mData->mSession.mDirectControl;
13385 }
13386
13387 /* ignore notifications sent after #OnSessionEnd() is called */
13388 if (!directControl)
13389 return S_OK;
13390 /*
13391 * instead acting like callback we ask IVirtualBox deliver corresponding event
13392 */
13393
13394 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13395 return S_OK;
13396}
13397
13398/**
13399 * @note Locks this object for reading.
13400 */
13401HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)
13402{
13403 LogFlowThisFunc(("\n"));
13404
13405 AutoCaller autoCaller(this);
13406 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13407
13408 ComPtr<IInternalSessionControl> directControl;
13409 {
13410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13411 directControl = mData->mSession.mDirectControl;
13412 }
13413
13414 /* ignore notifications sent after #OnSessionEnd() is called */
13415 if (!directControl)
13416 return S_OK;
13417
13418 return directControl->OnSerialPortChange(serialPort);
13419}
13420
13421/**
13422 * @note Locks this object for reading.
13423 */
13424HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)
13425{
13426 LogFlowThisFunc(("\n"));
13427
13428 AutoCaller autoCaller(this);
13429 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13430
13431 ComPtr<IInternalSessionControl> directControl;
13432 {
13433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13434 directControl = mData->mSession.mDirectControl;
13435 }
13436
13437 /* ignore notifications sent after #OnSessionEnd() is called */
13438 if (!directControl)
13439 return S_OK;
13440
13441 return directControl->OnParallelPortChange(parallelPort);
13442}
13443
13444/**
13445 * @note Locks this object for reading.
13446 */
13447HRESULT SessionMachine::onStorageControllerChange()
13448{
13449 LogFlowThisFunc(("\n"));
13450
13451 AutoCaller autoCaller(this);
13452 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13453
13454 ComPtr<IInternalSessionControl> directControl;
13455 {
13456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13457 directControl = mData->mSession.mDirectControl;
13458 }
13459
13460 /* ignore notifications sent after #OnSessionEnd() is called */
13461 if (!directControl)
13462 return S_OK;
13463
13464 return directControl->OnStorageControllerChange();
13465}
13466
13467/**
13468 * @note Locks this object for reading.
13469 */
13470HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13471{
13472 LogFlowThisFunc(("\n"));
13473
13474 AutoCaller autoCaller(this);
13475 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13476
13477 ComPtr<IInternalSessionControl> directControl;
13478 {
13479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13480 directControl = mData->mSession.mDirectControl;
13481 }
13482
13483 /* ignore notifications sent after #OnSessionEnd() is called */
13484 if (!directControl)
13485 return S_OK;
13486
13487 return directControl->OnMediumChange(aAttachment, aForce);
13488}
13489
13490/**
13491 * @note Locks this object for reading.
13492 */
13493HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)
13494{
13495 LogFlowThisFunc(("\n"));
13496
13497 AutoCaller autoCaller(this);
13498 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13499
13500 ComPtr<IInternalSessionControl> directControl;
13501 {
13502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13503 directControl = mData->mSession.mDirectControl;
13504 }
13505
13506 /* ignore notifications sent after #OnSessionEnd() is called */
13507 if (!directControl)
13508 return S_OK;
13509
13510 return directControl->OnCPUChange(aCPU, aRemove);
13511}
13512
13513HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap)
13514{
13515 LogFlowThisFunc(("\n"));
13516
13517 AutoCaller autoCaller(this);
13518 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13519
13520 ComPtr<IInternalSessionControl> directControl;
13521 {
13522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13523 directControl = mData->mSession.mDirectControl;
13524 }
13525
13526 /* ignore notifications sent after #OnSessionEnd() is called */
13527 if (!directControl)
13528 return S_OK;
13529
13530 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13531}
13532
13533/**
13534 * @note Locks this object for reading.
13535 */
13536HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart)
13537{
13538 LogFlowThisFunc(("\n"));
13539
13540 AutoCaller autoCaller(this);
13541 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13542
13543 ComPtr<IInternalSessionControl> directControl;
13544 {
13545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13546 directControl = mData->mSession.mDirectControl;
13547 }
13548
13549 /* ignore notifications sent after #OnSessionEnd() is called */
13550 if (!directControl)
13551 return S_OK;
13552
13553 return directControl->OnVRDEServerChange(aRestart);
13554}
13555
13556/**
13557 * @note Locks this object for reading.
13558 */
13559HRESULT SessionMachine::onUSBControllerChange()
13560{
13561 LogFlowThisFunc(("\n"));
13562
13563 AutoCaller autoCaller(this);
13564 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13565
13566 ComPtr<IInternalSessionControl> directControl;
13567 {
13568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13569 directControl = mData->mSession.mDirectControl;
13570 }
13571
13572 /* ignore notifications sent after #OnSessionEnd() is called */
13573 if (!directControl)
13574 return S_OK;
13575
13576 return directControl->OnUSBControllerChange();
13577}
13578
13579/**
13580 * @note Locks this object for reading.
13581 */
13582HRESULT SessionMachine::onSharedFolderChange()
13583{
13584 LogFlowThisFunc(("\n"));
13585
13586 AutoCaller autoCaller(this);
13587 AssertComRCReturnRC(autoCaller.rc());
13588
13589 ComPtr<IInternalSessionControl> directControl;
13590 {
13591 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13592 directControl = mData->mSession.mDirectControl;
13593 }
13594
13595 /* ignore notifications sent after #OnSessionEnd() is called */
13596 if (!directControl)
13597 return S_OK;
13598
13599 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13600}
13601
13602/**
13603 * @note Locks this object for reading.
13604 */
13605HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode)
13606{
13607 LogFlowThisFunc(("\n"));
13608
13609 AutoCaller autoCaller(this);
13610 AssertComRCReturnRC(autoCaller.rc());
13611
13612 ComPtr<IInternalSessionControl> directControl;
13613 {
13614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13615 directControl = mData->mSession.mDirectControl;
13616 }
13617
13618 /* ignore notifications sent after #OnSessionEnd() is called */
13619 if (!directControl)
13620 return S_OK;
13621
13622 return directControl->OnClipboardModeChange(aClipboardMode);
13623}
13624
13625/**
13626 * @note Locks this object for reading.
13627 */
13628HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode)
13629{
13630 LogFlowThisFunc(("\n"));
13631
13632 AutoCaller autoCaller(this);
13633 AssertComRCReturnRC(autoCaller.rc());
13634
13635 ComPtr<IInternalSessionControl> directControl;
13636 {
13637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13638 directControl = mData->mSession.mDirectControl;
13639 }
13640
13641 /* ignore notifications sent after #OnSessionEnd() is called */
13642 if (!directControl)
13643 return S_OK;
13644
13645 return directControl->OnDragAndDropModeChange(aDragAndDropMode);
13646}
13647
13648/**
13649 * @note Locks this object for reading.
13650 */
13651HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13652{
13653 LogFlowThisFunc(("\n"));
13654
13655 AutoCaller autoCaller(this);
13656 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13657
13658 ComPtr<IInternalSessionControl> directControl;
13659 {
13660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13661 directControl = mData->mSession.mDirectControl;
13662 }
13663
13664 /* ignore notifications sent after #OnSessionEnd() is called */
13665 if (!directControl)
13666 return S_OK;
13667
13668 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13669}
13670
13671/**
13672 * @note Locks this object for reading.
13673 */
13674HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
13675{
13676 LogFlowThisFunc(("\n"));
13677
13678 AutoCaller autoCaller(this);
13679 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13680
13681 ComPtr<IInternalSessionControl> directControl;
13682 {
13683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13684 directControl = mData->mSession.mDirectControl;
13685 }
13686
13687 /* ignore notifications sent after #OnSessionEnd() is called */
13688 if (!directControl)
13689 return S_OK;
13690
13691 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
13692}
13693
13694/**
13695 * Returns @c true if this machine's USB controller reports it has a matching
13696 * filter for the given USB device and @c false otherwise.
13697 *
13698 * @note locks this object for reading.
13699 */
13700bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
13701{
13702 AutoCaller autoCaller(this);
13703 /* silently return if not ready -- this method may be called after the
13704 * direct machine session has been called */
13705 if (!autoCaller.isOk())
13706 return false;
13707
13708#ifdef VBOX_WITH_USB
13709 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13710
13711 switch (mData->mMachineState)
13712 {
13713 case MachineState_Starting:
13714 case MachineState_Restoring:
13715 case MachineState_TeleportingIn:
13716 case MachineState_Paused:
13717 case MachineState_Running:
13718 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
13719 * elsewhere... */
13720 alock.release();
13721 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);
13722 default: break;
13723 }
13724#else
13725 NOREF(aDevice);
13726 NOREF(aMaskedIfs);
13727#endif
13728 return false;
13729}
13730
13731/**
13732 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13733 */
13734HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,
13735 IVirtualBoxErrorInfo *aError,
13736 ULONG aMaskedIfs)
13737{
13738 LogFlowThisFunc(("\n"));
13739
13740 AutoCaller autoCaller(this);
13741
13742 /* This notification may happen after the machine object has been
13743 * uninitialized (the session was closed), so don't assert. */
13744 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13745
13746 ComPtr<IInternalSessionControl> directControl;
13747 {
13748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13749 directControl = mData->mSession.mDirectControl;
13750 }
13751
13752 /* fail on notifications sent after #OnSessionEnd() is called, it is
13753 * expected by the caller */
13754 if (!directControl)
13755 return E_FAIL;
13756
13757 /* No locks should be held at this point. */
13758 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13759 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13760
13761 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);
13762}
13763
13764/**
13765 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
13766 */
13767HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,
13768 IVirtualBoxErrorInfo *aError)
13769{
13770 LogFlowThisFunc(("\n"));
13771
13772 AutoCaller autoCaller(this);
13773
13774 /* This notification may happen after the machine object has been
13775 * uninitialized (the session was closed), so don't assert. */
13776 if (FAILED(autoCaller.rc())) return autoCaller.rc();
13777
13778 ComPtr<IInternalSessionControl> directControl;
13779 {
13780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13781 directControl = mData->mSession.mDirectControl;
13782 }
13783
13784 /* fail on notifications sent after #OnSessionEnd() is called, it is
13785 * expected by the caller */
13786 if (!directControl)
13787 return E_FAIL;
13788
13789 /* No locks should be held at this point. */
13790 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
13791 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
13792
13793 return directControl->OnUSBDeviceDetach(aId, aError);
13794}
13795
13796// protected methods
13797/////////////////////////////////////////////////////////////////////////////
13798
13799/**
13800 * Helper method to finalize saving the state.
13801 *
13802 * @note Must be called from under this object's lock.
13803 *
13804 * @param aRc S_OK if the snapshot has been taken successfully
13805 * @param aErrMsg human readable error message for failure
13806 *
13807 * @note Locks mParent + this objects for writing.
13808 */
13809HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
13810{
13811 LogFlowThisFuncEnter();
13812
13813 AutoCaller autoCaller(this);
13814 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13815
13816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13817
13818 HRESULT rc = S_OK;
13819
13820 if (SUCCEEDED(aRc))
13821 {
13822 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
13823
13824 /* save all VM settings */
13825 rc = saveSettings(NULL);
13826 // no need to check whether VirtualBox.xml needs saving also since
13827 // we can't have a name change pending at this point
13828 }
13829 else
13830 {
13831 // delete the saved state file (it might have been already created);
13832 // we need not check whether this is shared with a snapshot here because
13833 // we certainly created this saved state file here anew
13834 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
13835 }
13836
13837 /* notify the progress object about operation completion */
13838 Assert(mConsoleTaskData.mProgress);
13839 if (SUCCEEDED(aRc))
13840 mConsoleTaskData.mProgress->notifyComplete(S_OK);
13841 else
13842 {
13843 if (aErrMsg.length())
13844 mConsoleTaskData.mProgress->notifyComplete(aRc,
13845 COM_IIDOF(ISession),
13846 getComponentName(),
13847 aErrMsg.c_str());
13848 else
13849 mConsoleTaskData.mProgress->notifyComplete(aRc);
13850 }
13851
13852 /* clear out the temporary saved state data */
13853 mConsoleTaskData.mLastState = MachineState_Null;
13854 mConsoleTaskData.strStateFilePath.setNull();
13855 mConsoleTaskData.mProgress.setNull();
13856
13857 LogFlowThisFuncLeave();
13858 return rc;
13859}
13860
13861/**
13862 * Deletes the given file if it is no longer in use by either the current machine state
13863 * (if the machine is "saved") or any of the machine's snapshots.
13864 *
13865 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
13866 * but is different for each SnapshotMachine. When calling this, the order of calling this
13867 * function on the one hand and changing that variable OR the snapshots tree on the other hand
13868 * is therefore critical. I know, it's all rather messy.
13869 *
13870 * @param strStateFile
13871 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
13872 */
13873void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
13874 Snapshot *pSnapshotToIgnore)
13875{
13876 // it is safe to delete this saved state file if it is not currently in use by the machine ...
13877 if ( (strStateFile.isNotEmpty())
13878 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
13879 )
13880 // ... and it must also not be shared with other snapshots
13881 if ( !mData->mFirstSnapshot
13882 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
13883 // this checks the SnapshotMachine's state file paths
13884 )
13885 RTFileDelete(strStateFile.c_str());
13886}
13887
13888/**
13889 * Locks the attached media.
13890 *
13891 * All attached hard disks are locked for writing and DVD/floppy are locked for
13892 * reading. Parents of attached hard disks (if any) are locked for reading.
13893 *
13894 * This method also performs accessibility check of all media it locks: if some
13895 * media is inaccessible, the method will return a failure and a bunch of
13896 * extended error info objects per each inaccessible medium.
13897 *
13898 * Note that this method is atomic: if it returns a success, all media are
13899 * locked as described above; on failure no media is locked at all (all
13900 * succeeded individual locks will be undone).
13901 *
13902 * The caller is responsible for doing the necessary state sanity checks.
13903 *
13904 * The locks made by this method must be undone by calling #unlockMedia() when
13905 * no more needed.
13906 */
13907HRESULT SessionMachine::lockMedia()
13908{
13909 AutoCaller autoCaller(this);
13910 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13911
13912 AutoMultiWriteLock2 alock(this->lockHandle(),
13913 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13914
13915 /* bail out if trying to lock things with already set up locking */
13916 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
13917
13918 MultiResult mrc(S_OK);
13919
13920 /* Collect locking information for all medium objects attached to the VM. */
13921 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
13922 it != mMediaData->mAttachments.end();
13923 ++it)
13924 {
13925 MediumAttachment* pAtt = *it;
13926 DeviceType_T devType = pAtt->getType();
13927 Medium *pMedium = pAtt->getMedium();
13928
13929 MediumLockList *pMediumLockList(new MediumLockList());
13930 // There can be attachments without a medium (floppy/dvd), and thus
13931 // it's impossible to create a medium lock list. It still makes sense
13932 // to have the empty medium lock list in the map in case a medium is
13933 // attached later.
13934 if (pMedium != NULL)
13935 {
13936 MediumType_T mediumType = pMedium->getType();
13937 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
13938 || mediumType == MediumType_Shareable;
13939 bool fIsVitalImage = (devType == DeviceType_HardDisk);
13940
13941 alock.release();
13942 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
13943 !fIsReadOnlyLock /* fMediumLockWrite */,
13944 NULL,
13945 *pMediumLockList);
13946 alock.acquire();
13947 if (FAILED(mrc))
13948 {
13949 delete pMediumLockList;
13950 mData->mSession.mLockedMedia.Clear();
13951 break;
13952 }
13953 }
13954
13955 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
13956 if (FAILED(rc))
13957 {
13958 mData->mSession.mLockedMedia.Clear();
13959 mrc = setError(rc,
13960 tr("Collecting locking information for all attached media failed"));
13961 break;
13962 }
13963 }
13964
13965 if (SUCCEEDED(mrc))
13966 {
13967 /* Now lock all media. If this fails, nothing is locked. */
13968 alock.release();
13969 HRESULT rc = mData->mSession.mLockedMedia.Lock();
13970 alock.acquire();
13971 if (FAILED(rc))
13972 {
13973 mrc = setError(rc,
13974 tr("Locking of attached media failed"));
13975 }
13976 }
13977
13978 return mrc;
13979}
13980
13981/**
13982 * Undoes the locks made by by #lockMedia().
13983 */
13984void SessionMachine::unlockMedia()
13985{
13986 AutoCaller autoCaller(this);
13987 AssertComRCReturnVoid(autoCaller.rc());
13988
13989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13990
13991 /* we may be holding important error info on the current thread;
13992 * preserve it */
13993 ErrorInfoKeeper eik;
13994
13995 HRESULT rc = mData->mSession.mLockedMedia.Clear();
13996 AssertComRC(rc);
13997}
13998
13999/**
14000 * Helper to change the machine state (reimplementation).
14001 *
14002 * @note Locks this object for writing.
14003 * @note This method must not call saveSettings or SaveSettings, otherwise
14004 * it can cause crashes in random places due to unexpectedly committing
14005 * the current settings. The caller is responsible for that. The call
14006 * to saveStateSettings is fine, because this method does not commit.
14007 */
14008HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
14009{
14010 LogFlowThisFuncEnter();
14011 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14012
14013 AutoCaller autoCaller(this);
14014 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14015
14016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14017
14018 MachineState_T oldMachineState = mData->mMachineState;
14019
14020 AssertMsgReturn(oldMachineState != aMachineState,
14021 ("oldMachineState=%s, aMachineState=%s\n",
14022 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14023 E_FAIL);
14024
14025 HRESULT rc = S_OK;
14026
14027 int stsFlags = 0;
14028 bool deleteSavedState = false;
14029
14030 /* detect some state transitions */
14031
14032 if ( ( oldMachineState == MachineState_Saved
14033 && aMachineState == MachineState_Restoring)
14034 || ( ( oldMachineState == MachineState_PoweredOff
14035 || oldMachineState == MachineState_Teleported
14036 || oldMachineState == MachineState_Aborted
14037 )
14038 && ( aMachineState == MachineState_TeleportingIn
14039 || aMachineState == MachineState_Starting
14040 )
14041 )
14042 )
14043 {
14044 /* The EMT thread is about to start */
14045
14046 /* Nothing to do here for now... */
14047
14048 /// @todo NEWMEDIA don't let mDVDDrive and other children
14049 /// change anything when in the Starting/Restoring state
14050 }
14051 else if ( ( oldMachineState == MachineState_Running
14052 || oldMachineState == MachineState_Paused
14053 || oldMachineState == MachineState_Teleporting
14054 || oldMachineState == MachineState_LiveSnapshotting
14055 || oldMachineState == MachineState_Stuck
14056 || oldMachineState == MachineState_Starting
14057 || oldMachineState == MachineState_Stopping
14058 || oldMachineState == MachineState_Saving
14059 || oldMachineState == MachineState_Restoring
14060 || oldMachineState == MachineState_TeleportingPausedVM
14061 || oldMachineState == MachineState_TeleportingIn
14062 )
14063 && ( aMachineState == MachineState_PoweredOff
14064 || aMachineState == MachineState_Saved
14065 || aMachineState == MachineState_Teleported
14066 || aMachineState == MachineState_Aborted
14067 )
14068 /* ignore PoweredOff->Saving->PoweredOff transition when taking a
14069 * snapshot */
14070 && ( mConsoleTaskData.mSnapshot.isNull()
14071 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */
14072 )
14073 )
14074 {
14075 /* The EMT thread has just stopped, unlock attached media. Note that as
14076 * opposed to locking that is done from Console, we do unlocking here
14077 * because the VM process may have aborted before having a chance to
14078 * properly unlock all media it locked. */
14079
14080 unlockMedia();
14081 }
14082
14083 if (oldMachineState == MachineState_Restoring)
14084 {
14085 if (aMachineState != MachineState_Saved)
14086 {
14087 /*
14088 * delete the saved state file once the machine has finished
14089 * restoring from it (note that Console sets the state from
14090 * Restoring to Saved if the VM couldn't restore successfully,
14091 * to give the user an ability to fix an error and retry --
14092 * we keep the saved state file in this case)
14093 */
14094 deleteSavedState = true;
14095 }
14096 }
14097 else if ( oldMachineState == MachineState_Saved
14098 && ( aMachineState == MachineState_PoweredOff
14099 || aMachineState == MachineState_Aborted
14100 || aMachineState == MachineState_Teleported
14101 )
14102 )
14103 {
14104 /*
14105 * delete the saved state after Console::ForgetSavedState() is called
14106 * or if the VM process (owning a direct VM session) crashed while the
14107 * VM was Saved
14108 */
14109
14110 /// @todo (dmik)
14111 // Not sure that deleting the saved state file just because of the
14112 // client death before it attempted to restore the VM is a good
14113 // thing. But when it crashes we need to go to the Aborted state
14114 // which cannot have the saved state file associated... The only
14115 // way to fix this is to make the Aborted condition not a VM state
14116 // but a bool flag: i.e., when a crash occurs, set it to true and
14117 // change the state to PoweredOff or Saved depending on the
14118 // saved state presence.
14119
14120 deleteSavedState = true;
14121 mData->mCurrentStateModified = TRUE;
14122 stsFlags |= SaveSTS_CurStateModified;
14123 }
14124
14125 if ( aMachineState == MachineState_Starting
14126 || aMachineState == MachineState_Restoring
14127 || aMachineState == MachineState_TeleportingIn
14128 )
14129 {
14130 /* set the current state modified flag to indicate that the current
14131 * state is no more identical to the state in the
14132 * current snapshot */
14133 if (!mData->mCurrentSnapshot.isNull())
14134 {
14135 mData->mCurrentStateModified = TRUE;
14136 stsFlags |= SaveSTS_CurStateModified;
14137 }
14138 }
14139
14140 if (deleteSavedState)
14141 {
14142 if (mRemoveSavedState)
14143 {
14144 Assert(!mSSData->strStateFilePath.isEmpty());
14145
14146 // it is safe to delete the saved state file if ...
14147 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14148 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14149 // ... none of the snapshots share the saved state file
14150 )
14151 RTFileDelete(mSSData->strStateFilePath.c_str());
14152 }
14153
14154 mSSData->strStateFilePath.setNull();
14155 stsFlags |= SaveSTS_StateFilePath;
14156 }
14157
14158 /* redirect to the underlying peer machine */
14159 mPeer->setMachineState(aMachineState);
14160
14161 if ( aMachineState == MachineState_PoweredOff
14162 || aMachineState == MachineState_Teleported
14163 || aMachineState == MachineState_Aborted
14164 || aMachineState == MachineState_Saved)
14165 {
14166 /* the machine has stopped execution
14167 * (or the saved state file was adopted) */
14168 stsFlags |= SaveSTS_StateTimeStamp;
14169 }
14170
14171 if ( ( oldMachineState == MachineState_PoweredOff
14172 || oldMachineState == MachineState_Aborted
14173 || oldMachineState == MachineState_Teleported
14174 )
14175 && aMachineState == MachineState_Saved)
14176 {
14177 /* the saved state file was adopted */
14178 Assert(!mSSData->strStateFilePath.isEmpty());
14179 stsFlags |= SaveSTS_StateFilePath;
14180 }
14181
14182#ifdef VBOX_WITH_GUEST_PROPS
14183 if ( aMachineState == MachineState_PoweredOff
14184 || aMachineState == MachineState_Aborted
14185 || aMachineState == MachineState_Teleported)
14186 {
14187 /* Make sure any transient guest properties get removed from the
14188 * property store on shutdown. */
14189
14190 HWData::GuestPropertyMap::const_iterator it;
14191 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14192 if (!fNeedsSaving)
14193 for (it = mHWData->mGuestProperties.begin();
14194 it != mHWData->mGuestProperties.end(); ++it)
14195 if ( (it->second.mFlags & guestProp::TRANSIENT)
14196 || (it->second.mFlags & guestProp::TRANSRESET))
14197 {
14198 fNeedsSaving = true;
14199 break;
14200 }
14201 if (fNeedsSaving)
14202 {
14203 mData->mCurrentStateModified = TRUE;
14204 stsFlags |= SaveSTS_CurStateModified;
14205 }
14206 }
14207#endif
14208
14209 rc = saveStateSettings(stsFlags);
14210
14211 if ( ( oldMachineState != MachineState_PoweredOff
14212 && oldMachineState != MachineState_Aborted
14213 && oldMachineState != MachineState_Teleported
14214 )
14215 && ( aMachineState == MachineState_PoweredOff
14216 || aMachineState == MachineState_Aborted
14217 || aMachineState == MachineState_Teleported
14218 )
14219 )
14220 {
14221 /* we've been shut down for any reason */
14222 /* no special action so far */
14223 }
14224
14225 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14226 LogFlowThisFuncLeave();
14227 return rc;
14228}
14229
14230/**
14231 * Sends the current machine state value to the VM process.
14232 *
14233 * @note Locks this object for reading, then calls a client process.
14234 */
14235HRESULT SessionMachine::updateMachineStateOnClient()
14236{
14237 AutoCaller autoCaller(this);
14238 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14239
14240 ComPtr<IInternalSessionControl> directControl;
14241 {
14242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14243 AssertReturn(!!mData, E_FAIL);
14244 directControl = mData->mSession.mDirectControl;
14245
14246 /* directControl may be already set to NULL here in #OnSessionEnd()
14247 * called too early by the direct session process while there is still
14248 * some operation (like deleting the snapshot) in progress. The client
14249 * process in this case is waiting inside Session::close() for the
14250 * "end session" process object to complete, while #uninit() called by
14251 * #checkForDeath() on the Watcher thread is waiting for the pending
14252 * operation to complete. For now, we accept this inconsistent behavior
14253 * and simply do nothing here. */
14254
14255 if (mData->mSession.mState == SessionState_Unlocking)
14256 return S_OK;
14257
14258 AssertReturn(!directControl.isNull(), E_FAIL);
14259 }
14260
14261 return directControl->UpdateMachineState(mData->mMachineState);
14262}
Note: See TracBrowser for help on using the repository browser.

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